diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..7660454bf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +static/ +tmp/ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..6313b56c5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.gitignore b/.gitignore index 8158a50d6..4423fc434 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,5 @@ development/ package-lock.json src/config.json src/config-template.json + +.wvr-ud/static-assets/styles.css diff --git a/.wvr-ud/config.json b/.wvr-ud/config.json index ea66417ee..5036db394 100644 --- a/.wvr-ud/config.json +++ b/.wvr-ud/config.json @@ -8,7 +8,7 @@ "projects/wvr-elements/src/lib/wvr-it-works" ], "additionalScript": [ - "dist/bundle/latest/weaver-components.js" + "dist/bundle/weaver-components.js" ], "additionalAssets": [ "src/assets/**/*" diff --git a/.wvr-ud/static-assets/styles.css b/.wvr-ud/static-assets/_styles.css similarity index 99% rename from .wvr-ud/static-assets/styles.css rename to .wvr-ud/static-assets/_styles.css index dbff072dc..5de57f430 100644 --- a/.wvr-ud/static-assets/styles.css +++ b/.wvr-ud/static-assets/_styles.css @@ -148,4 +148,6 @@ main.loading #preview { .feather-plus-circle, .open .feather-minus-circle { display: inline-block; -} \ No newline at end of file +} + +/*** OVERRIDES ***/ diff --git a/.wvr-ud/static-assets/index-base.html b/.wvr-ud/static-assets/index-base.html index e546198f5..f53308693 100644 --- a/.wvr-ud/static-assets/index-base.html +++ b/.wvr-ud/static-assets/index-base.html @@ -4,7 +4,7 @@ {{PROJECT_NAME}} - + diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f6da6fc0..63a32e337 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,71 @@ # Changelog +## [1.6.0] - 11-2-20 +### Resolves + +- Weaver component's build process should build multiple versions of the product. (#86) +- Weaver Usage Docs should be converted into an angular project. (#102) +- The wvr manifest action to submit request should afford providing response map. (#223) +- Handlebars should be extracted into a service and all helpers declared outside of the component constructor. (#232) +- On safari (check firefox) mobile display does not run the position footer function at load time. (#233) +- Weaver components should provide a wvr-cards component. (#234) +- Weaver Component selectors need to be refactored to avoid name collision with Weaver (angularjs) directives. (#237) +- The wvr-header should use the configured asset path for the header logo. (#255) + +## [1.5.0] - 10-8-20 +### Resolves + +- Weaver Components should communicate with Weaver web services. (#195) +- Weaver Lists should automatically assign aria owns attribute for each of its list items. (#206) +- Weaver Components should provide an Alert Component. (#207) +- Weaver Components should dynamically assign unique identifiers to each component. (#208) +- Explore ngRX as a state store solution for components. (#209) +- Weaver Components should offer a tabs component. (#210) + +## [1.4.0] - 9-23-20 +### Resolves + +- Encapsulates bootstrap within global styling +- Performance improvements +- Introduces Mobile Service +- The list item context attr should override the visual context of the list item. (#199) + ## [1.3.0] - 08-13-20 ### Resolves -Weaver Components should have an element query strategy. (#27) -Icons should be integrated into weaver buttons. (#128) -Weaver Components should support lists with expanding and collapsing sections. (#163) -The Wvr Icon component should support animation. (#164) -Weaver Components should have a list component. (#165) -Coveralls not providing the coverage notification. (#167) -Observable subscriptions should be unsubscribed from or converted to promises. (#168) +- Weaver Components should have an element query strategy. (#27) +- Icons should be integrated into weaver buttons. (#128) +- Weaver Components should support lists with expanding and collapsing sections. (#163) +- The Wvr Icon component should support animation. (#164) +- Weaver Components should have a list component. (#165) +- Coveralls not providing the coverage notification. (#167) +- Observable subscriptions should be unsubscribed from or converted to promises. (#168) + +## [1.2.0] - 07-16-20 +### Resolves + +- Weaver Component should support a global configuration paradigm. (#64) +- Weaver Components should share vendor styling across components. (#82) +- Weaver Components should support multiple font icon libraries. (#85) +- Buttons must be integrated into weaver drop-down. (#129) +- Weaver footer "stick-to-bottom" property affected by wvr-button line-height and padding property. (#150) + +## [1.1.0] - 06-25-20 +### Resolves + +- Wvr Components should provide a wvr-button component (#103) +- CDN needs to be exposed through the firewall (#135) +- Fix Z-Index on dropdown overlaps (#133) +- Restore dynamic change detection for display of bottom nav (#136) +- Dropdown inside of List inside of Header flickers instead of staying open (#119) + +## [1.0.0] - 06-11-20 +### Resolves + +- The wvr-header should support mobile and tablet layouts (#7) +- Weaver Components should have CI hooks for lighthouse (#105) +- wvr-component should have attribute: page-title-url (#116) +- Weaver Drop down component selector should be renamed (#125) ## [0.0.5] - 05-14-2020 ### Resolves diff --git a/Dockerfile b/Dockerfile index ced8e4e65..67a753a04 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,13 +8,18 @@ COPY . . RUN npm install RUN npm run build -RUN ls -la - FROM httpd:2.4-alpine -COPY --from=npm /app/dist/bundle/ /usr/local/apache2/htdocs/wvr-components/ +ARG MAJOR_VERSION=0x +ARG MAJOR_MINOR_VERSION=0.0 + +COPY --from=npm /app/dist/bundle/ /usr/local/apache2/htdocs/wvr-components/bundle + +RUN ln -s /usr/local/apache2/htdocs/wvr-components/bundle /usr/local/apache2/htdocs/wvr-components/latest +RUN ln -s /usr/local/apache2/htdocs/wvr-components/bundle /usr/local/apache2/htdocs/wvr-components/${MAJOR_VERSION}x +RUN ln -s /usr/local/apache2/htdocs/wvr-components/bundle /usr/local/apache2/htdocs/wvr-components/${MAJOR_MINOR_VERSION} -COPY src/config-template.json tmp/config-template.json +COPY --from=npm /app/src/config-template.json tmp/config-template.json COPY docker-entrypoint /usr/local/bin/ RUN chmod +x /usr/local/bin/docker-entrypoint diff --git a/angular.json b/angular.json index 982219d60..825d3818f 100644 --- a/angular.json +++ b/angular.json @@ -39,7 +39,8 @@ } ], "allowedCommonJsDependencies": [ - "css-element-queries" + "css-element-queries", + "handlebars" ] }, "configurations": { diff --git a/defaults.env b/defaults.env index e96627ed1..7bdf49605 100644 --- a/defaults.env +++ b/defaults.env @@ -3,5 +3,5 @@ # found at src/config-template.json. # -BASE_URL=https://labs.library.tamu.edu/wvr-components/latest -ASSETS_URL=https://labs.library.tamu.edu/wvr-components/latest/assets +BASE_URL=https://labs.library.tamu.edu/wvr-components +ASSETS_URL=https://labs.library.tamu.edu/wvr-components/assets diff --git a/docker-entrypoint b/docker-entrypoint index 398086cd1..f2465ab47 100755 --- a/docker-entrypoint +++ b/docker-entrypoint @@ -1,13 +1,11 @@ -#!/bin/bash +#!/bin/sh set -e -versionLocation=/usr/local/apache2/htdocs/wvr-components/1x/config.json -latestLocation=/usr/local/apache2/htdocs/wvr-components/latest/config.json +config=/usr/local/apache2/htdocs/wvr-components/bundle/config.json +template=tmp/config-template.json -eval "cat << EOF | tee $versionLocation $latestLocation -$( $config echo "Done docker-entrypoint..." diff --git a/e2e/src/app.po.ts b/e2e/src/app.po.ts index 6b7d1fe79..bcfde214c 100644 --- a/e2e/src/app.po.ts +++ b/e2e/src/app.po.ts @@ -6,7 +6,7 @@ export class AppPage { } async getItWorksText(): Promise { - return element(by.css('wvr-it-works')) + return element(by.css('wvre-it-works')) .getText() as Promise; } } diff --git a/package.json b/package.json index 89ceee5bd..190693874 100644 --- a/package.json +++ b/package.json @@ -4,16 +4,11 @@ "type": "git", "url": "git+https://github.com/TAMULib/weaver-components.git" }, - "version": "1.5.0", + "version": "1.6.0", "private": false, "license": "MIT", - "bin": { - "wvr-ud": "./scripts/build-wvr-ud.js", - "wvr-build-components": ".scripts/build-wvr-components.js", - "wvr-build-configuration": ".scripts/build-wvr-components-configuration.js", - "wvr-build-lighthouse": ".scripts/build-wvr-components-lighthouse-badges.js", - "wvr-build-static": ".scripts/build-wvr-components-static.js", - "wvr-serve-dist": ".scripts/serve-dist.js" + "config": { + "DOCKER_SERVER": "" }, "scripts": { "build": "ng build --prod --output-hashing none", @@ -27,19 +22,25 @@ "build:docs": "npm run build:docs-usage && npm run build:docs-development", "build:docs-development": "compodoc --output ./static/weaver-components/docs/development -p ./projects/wvr-elements/tsconfig.docs.json", "build:docs-usage": "node scripts/build-wvr-ud.js", + "build:docker": "node scripts/build-docker.js", "build:static": "npm run build:static-docs && npm run build:static-reports", "build:static-setup": "node scripts/build-wvr-components-static.js", "build:static-docs": "npm run test:audit && npm run build:static-setup && npm run build:docs-usage && npm run build:docs-development", "build:static-reports": "npm run build:static-setup && npm run test:coverage", "build:static-production": "npm run build:static && node scripts/build-wvr-components-configuration.js defaults-ci-overrides.env && ls -A | grep -v static | xargs rimraf && mv static/weaver-components/* . && rimraf static", - "clean": "rimraf dist", + "clean": "npm run clean:dist && npm run clean:static", + "clean:dist": "rimraf dist", "clean:static": "rimraf static", + "publish:docker": "node scripts/docker-push.js", + "publish:npm": "npm run build && node scripts/build-publish.js $1", + "publish:npm-next": "npm run publish:npm next", "lint": "ng lint", "link": "npm link ./dist/wvr-elements", "ng": "ng", "start": "node scripts/build-wvr-components-configuration.js defaults-dev-overrides.env && ng serve --port 4200", "start:dist": "node scripts/build-wvr-components-configuration.js defaults-dist-overrides.env && node scripts/serve-dist.js", "start:static": "node scripts/build-wvr-components-configuration.js defaults-static-overrides.env && static-server static -p 8081", + "start:docker": "node scripts/start-docker.js", "test": "npm run test:unit && npm run test:e2e", "test:audit": "rimraf .lighthouseci && lhci autorun --upload.target=temporary-public-storage --config=./lighthouserc.json && node scripts/build-wvr-components-lighthouse-badges.js", "test:e2e": "ng e2e", @@ -50,69 +51,78 @@ "test:ci": "npm run test:coverage && npm run test:audit" }, "dependencies": { - "@angular/animations": "^10.0.12", - "@angular/common": "^10.0.12", - "@angular/compiler": "^10.0.12", - "@angular/core": "^10.0.12", - "@angular/elements": "^10.0.12", - "@angular/forms": "^10.0.12", - "@angular/localize": "^10.0.12", - "@angular/platform-browser": "^10.0.12", - "@angular/platform-browser-dynamic": "^10.0.12", - "@angular/router": "^10.0.12", + "@angular/animations": "^10.2.0", + "@angular/common": "^10.2.0", + "@angular/compiler": "^10.2.0", + "@angular/core": "^10.2.0", + "@angular/elements": "^10.2.0", + "@angular/forms": "^10.2.0", + "@angular/localize": "^10.2.0", + "@angular/platform-browser": "^10.2.0", + "@angular/platform-browser-dynamic": "^10.2.0", + "@angular/router": "^10.2.0", "@ng-bootstrap/ng-bootstrap": "^7.0.0", + "@ngrx/core": "^1.2.0", + "@ngrx/effects": "^10.0.1", + "@ngrx/entity": "^10.0.1", + "@ngrx/router-store": "^10.0.1", + "@ngrx/store": "^10.0.1", + "@ngrx/store-devtools": "^10.0.1", "@types/json5": "0.0.30", - "dotenv-override": "^5.0.1", - "dotenv-override-true": "^6.2.1", - "bootstrap": "^4.5.2", + "bootstrap": "^4.5.3", "classlist.js": "^1.1.20150312", "css-element-queries": "^1.2.3", "document-register-element": "^1.14.5", + "dotenv-override": "^5.0.1", + "dotenv-override-true": "^6.2.2", + "handlebars": "^4.7.6", "ie11-custom-properties": "^4.1.0", "json5": "^2.1.3", - "rxjs": "~6.6.2", - "tslib": "^2.0.1", + "rxjs": "~6.6.3", + "tslib": "^2.0.3", "web-animations-js": "^2.3.2", - "zone.js": "~0.10.3" + "zone.js": "~0.11.2" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.1000.7", - "@angular-devkit/build-ng-packagr": "~0.1000.7", - "@angular-devkit/schematics": "^10.0.7", - "@angular/cli": "^10.0.7", - "@angular/compiler-cli": "^10.0.12", - "@angular/language-service": "^10.0.12", + "@angular-devkit/build-angular": "^0.1002.0", + "@angular-devkit/build-ng-packagr": "~0.1001.7", + "@angular-devkit/schematics": "^10.1.7", + "@angular/cli": "^10.1.7", + "@angular/compiler-cli": "^10.2.0", + "@angular/language-service": "^10.2.0", "@compodoc/compodoc": "^1.1.11", - "@lhci/cli": "^0.5.0", + "@lhci/cli": "^0.5.1", + "@ngrx/schematics": "^10.0.1", "@pickra/copy-code-block": "^1.2.0", - "@types/jasmine": "~3.5.13", + "@types/jasmine": "~3.5.14", "@types/jasminewd2": "~2.0.8", - "@types/node": "^14.6.0", + "@types/node": "^14.14.2", "angular-tslint-rules": "^1.20.4", "chalk": "^4.1.0", "chrome-launcher": "^0.13.4", - "codelyzer": "^6.0.0", + "codelyzer": "^6.0.1", "concat": "^1.0.3", "console-stamp": "^3.0.0-rc4.2", "coveralls": "^3.1.0", "glob": "^7.1.6", "jasmine-core": "~3.6.0", - "jasmine-spec-reporter": "~5.0.2", + "jasmine-spec-reporter": "~6.0.0", "jsdom": "^16.4.0", - "karma": "~5.1.1", + "karma": "~5.2.3", "karma-chrome-launcher": "~3.1.0", "karma-coverage-istanbul-reporter": "~3.0.3", "karma-jasmine": "~4.0.1", "karma-jasmine-html-reporter": "^1.5.4", - "lighthouse": "^6.2.0", - "lighthouse-badges": "^1.0.33", - "ng-packagr": "^10.0.4", + "lighthouse": "^6.4.1", + "lighthouse-badges": "^1.0.36", + "ng-packagr": "^10.1.2", "protractor": "~7.0.0", "rimraf": "^3.0.2", + "shelljs": "^0.8.4", "static-server": "^2.2.1", - "ts-loader": "^8.0.3", + "ts-loader": "^8.0.6", "ts-node": "~9.0.0", "tslint": "~6.1.3", - "typescript": "~3.9.4" + "typescript": "~4.0.3" } } diff --git a/projects/wvr-elements/package.json b/projects/wvr-elements/package.json index 5089092a9..34670106e 100644 --- a/projects/wvr-elements/package.json +++ b/projects/wvr-elements/package.json @@ -1,6 +1,6 @@ { "name": "@wvr/elements", - "version": "1.5.0", + "version": "1.6.0", "description": "Collection of angular components for Weaver's Custom Web Component UI", "author": "Texas A&M University Libraries", "private": false, @@ -21,9 +21,12 @@ ], "license": "MIT", "dependencies": { - "css-element-queries": "^1.2.3" - , "tslib": "^2.0.0" -}, + "css-element-queries": "^1.2.3", + "tslib": "^2.0.0" + }, + "bin": { + "wvr-ud": "./scripts/build-wvr-ud.js" + }, "peerDependencies": { "@angular/common": "^9.0.0", "@angular/core": "^9.0.0" diff --git a/projects/wvr-elements/src/lib/core/component-registry.service.spec.ts b/projects/wvr-elements/src/lib/core/component-registry.service.spec.ts index bd0c7f267..6542deb69 100644 --- a/projects/wvr-elements/src/lib/core/component-registry.service.spec.ts +++ b/projects/wvr-elements/src/lib/core/component-registry.service.spec.ts @@ -1,19 +1,20 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { WvrBaseComponent } from '../shared/wvr-base.component'; +import { StoreModule } from '@ngrx/store'; import { WvrItWorksComponent } from '../wvr-it-works/wvr-it-works.component'; import { ComponentRegistryService } from './component-registry.service'; +import { metaReducers, ROOT_REDUCER } from './store'; describe('ComponentRegistryService', () => { - let service: ComponentRegistryService; + let service: ComponentRegistryService; let component: WvrItWorksComponent; let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], declarations: [WvrItWorksComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) @@ -43,7 +44,7 @@ describe('ComponentRegistryService', () => { .toBeTruthy(); }); - it('should retrieve components by element', () => { + it('should retrieve components by component', () => { // tslint:disable-next-line:no-string-literal expect(service.getComponentByElement(component['_eRef'].nativeElement as HTMLElement) === component) .toBeTruthy(); diff --git a/projects/wvr-elements/src/lib/core/component-registry.service.ts b/projects/wvr-elements/src/lib/core/component-registry.service.ts index a28b489be..92736b668 100644 --- a/projects/wvr-elements/src/lib/core/component-registry.service.ts +++ b/projects/wvr-elements/src/lib/core/component-registry.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { WvrBaseComponent } from '../shared/wvr-base.component'; /** * A registry for each WvrBaseComponent currently present on the page. @@ -7,16 +6,19 @@ import { WvrBaseComponent } from '../shared/wvr-base.component'; @Injectable({ providedIn: 'root' }) -export class ComponentRegistryService { +export class ComponentRegistryService { /** Incrementing index of all registered components. */ private index = -1; /** Registry for all WvrBaseComponent. */ - private readonly registry: Map = new Map(); + private readonly registry: Map = new Map(); + + /** A statically accessible reference to the prefix used in deriving the HTML identifier. */ + static readonly HTML_ID_BASE = 'wvr-component'; /** Adds a WvrBaseComponent to the registry. */ - register(component: WvrBaseComponent): number { + register(component: T): number { // tslint:disable-next-line:no-string-literal const element = (component['_eRef'].nativeElement as HTMLElement); @@ -32,12 +34,12 @@ export class ComponentRegistryService { } /** Retrieves a WvrBaseComponent from the registry. */ - getComponent(id: number | string): WvrBaseComponent { + getComponent(id: number | string): T { return this.registry.get(id); } /** Retrieves a WvrBaseComponent from the registry by HTMLElement. */ - getComponentByElement(element: HTMLElement): WvrBaseComponent { + getComponentByElement(element: HTMLElement): T { const hasNativeId = element.hasAttribute('wvr-id'); const htmlID = hasNativeId ? element.getAttribute('wvr-id') : element.getAttribute('id'); @@ -46,7 +48,7 @@ export class ComponentRegistryService { return; } - const id = parseInt(htmlID.replace(`${WvrBaseComponent.HTML_ID_BASE}-`, ''), 10); + const id = parseInt(htmlID.replace(`${ComponentRegistryService.HTML_ID_BASE}-`, ''), 10); return this.getComponent(id); } diff --git a/projects/wvr-elements/src/lib/core/data-select.ts b/projects/wvr-elements/src/lib/core/data-select.ts new file mode 100644 index 000000000..723dd77d3 --- /dev/null +++ b/projects/wvr-elements/src/lib/core/data-select.ts @@ -0,0 +1,5 @@ +export interface WvrDataSelect { + manifest: string; + entry: string; + as: string; +} diff --git a/projects/wvr-elements/src/lib/core/handlebars-helpers.ts b/projects/wvr-elements/src/lib/core/handlebars-helpers.ts new file mode 100644 index 000000000..7d4a9fde8 --- /dev/null +++ b/projects/wvr-elements/src/lib/core/handlebars-helpers.ts @@ -0,0 +1,8 @@ + +/** + * All helpers available in templating. + * DEV NOTE: New helpers must be registered in TemplateService. + */ +export const handlebarsHelpers = { + json: context => JSON.stringify(context) +}; diff --git a/projects/wvr-elements/src/lib/core/icon.service.spec.ts b/projects/wvr-elements/src/lib/core/icon.service.spec.ts index ec26f2d57..211db2d53 100644 --- a/projects/wvr-elements/src/lib/core/icon.service.spec.ts +++ b/projects/wvr-elements/src/lib/core/icon.service.spec.ts @@ -1,15 +1,17 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; +import { StoreModule } from '@ngrx/store'; import { APP_CONFIG } from '../shared/config/app-config'; import { testAppConfig } from '../shared/config/test-app-config'; import { IconService } from './icon.service'; +import { metaReducers, ROOT_REDUCER } from './store'; describe('IconService', () => { let service: IconService; beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], + imports: [HttpClientTestingModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], providers: [{ provide: APP_CONFIG, useValue: testAppConfig diff --git a/projects/wvr-elements/src/lib/core/manifest/manifest-entry-request.ts b/projects/wvr-elements/src/lib/core/manifest/manifest-entry-request.ts new file mode 100644 index 000000000..12aa892a2 --- /dev/null +++ b/projects/wvr-elements/src/lib/core/manifest/manifest-entry-request.ts @@ -0,0 +1,13 @@ +import { Action } from '@ngrx/store'; +import { RequestMethod } from '../rest/request-method'; +import { RequestOptions } from '../rest/request-options'; + +export interface ManifestEntryRequest { + manifestName?: string; + entryName: string; + method?: RequestMethod; + body?: any; + options?: RequestOptions; + onSuccess?: Array; + onFailure?: Array; +} diff --git a/projects/wvr-elements/src/lib/core/manifest/manifest-entry.ts b/projects/wvr-elements/src/lib/core/manifest/manifest-entry.ts new file mode 100644 index 000000000..38cdb9897 --- /dev/null +++ b/projects/wvr-elements/src/lib/core/manifest/manifest-entry.ts @@ -0,0 +1,21 @@ +import { RequestMethod } from '../rest/request-method'; +import { RequestOptions } from '../rest/request-options'; + +export type map = (response) => any; + +export interface ManifestEntry { + name: string; // unique + description?: string; + path: string; + // allowed methods for entry + methods: Array; + // options defined by manifest to be merged with request + options?: RequestOptions; + // last request submitted + request?: any; + // response from last successful request + response?: any; + // error from last failed request + error?: any; + map?: map; +} diff --git a/projects/wvr-elements/src/lib/core/manifest/manifest.actions.ts b/projects/wvr-elements/src/lib/core/manifest/manifest.actions.ts new file mode 100644 index 000000000..94e0af4a9 --- /dev/null +++ b/projects/wvr-elements/src/lib/core/manifest/manifest.actions.ts @@ -0,0 +1,60 @@ +import { EntityMap, EntityMapOne, Predicate, Update } from '@ngrx/entity'; +import { createAction, props } from '@ngrx/store'; +import { Manifest } from './manifest'; +import { ManifestEntryRequest } from './manifest-entry-request'; + +export const loadManifests = createAction('[Manifest] Load Manifests', props<{ manifests: Array }>()); +export const addManifest = createAction('[Manifest] Add Manifest', props<{ manifest: Manifest }>()); +export const setManifest = createAction('[Manifest] Set Manifest', props<{ manifest: Manifest }>()); +export const upsertManifest = createAction('[Manifest] Upsert Manifest', props<{ manifest: Manifest }>()); +export const addManifests = createAction('[Manifest] Add Manifests', props<{ manifests: Array }>()); +export const upsertManifests = createAction('[Manifest] Upsert Manifests', props<{ manifests: Array }>()); +export const updateManifest = createAction('[Manifest] Update Manifest', props<{ update: Update }>()); +export const updateManifests = createAction('[Manifest] Update Manifests', props<{ updates: Array> }>()); +export const mapManifest = createAction('[Manifest] Map Manifest', props<{ entityMap: EntityMapOne }>()); +export const mapManifests = createAction('[Manifest] Map Manifests', props<{ entityMap: EntityMap }>()); +export const deleteManifest = createAction('[Manifest] Delete Manifest', props<{ id: string }>()); +export const deleteManifests = createAction('[Manifest] Delete Manifests', props<{ ids: Array }>()); +export const deleteManifestsByPredicate = createAction( + '[Manifest] Delete Manifests By Predicate', + props<{ predicate: Predicate }>()); +export const clearManifests = createAction('[Manifest] Clear Manifests'); + +export const submitRequest = createAction( + '[Manifest] Submit Request', + props<{ + request: ManifestEntryRequest + }>() +); + +export const submitRequestSuccess = createAction( + '[Manifest] Submit Request Success', + props<{ + manifest: Manifest, + request: ManifestEntryRequest, + response: any + }>() +); + +export const submitRequestFailure = createAction( + '[Manifest] Submit Request Failure', + props<{ + manifest: Manifest, + request: ManifestEntryRequest, + error: any + }>() +); + +export const queueRequest = createAction( + '[Manifest] Queue Request', + props<{ + request: ManifestEntryRequest + }>() +); + +export const dequeueRequest = createAction( + '[Manifest] Dequeue Request', + props<{ + request: ManifestEntryRequest + }>() +); diff --git a/projects/wvr-elements/src/lib/core/manifest/manifest.effects.ts b/projects/wvr-elements/src/lib/core/manifest/manifest.effects.ts new file mode 100644 index 000000000..94101ed5e --- /dev/null +++ b/projects/wvr-elements/src/lib/core/manifest/manifest.effects.ts @@ -0,0 +1,123 @@ +import { Injectable } from '@angular/core'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; +import { select, Store } from '@ngrx/store'; +import { combineLatest, of } from 'rxjs'; +import { map, mergeMap, pluck, switchMap, take } from 'rxjs/operators'; +import { RequestMethod } from '../rest/request-method'; +import * as RestActions from '../rest/rest.actions'; +import { RootState, selectManifestByName, selectManifestEntities, selectPendingRequests } from '../store'; +import { Manifest } from './manifest'; +import * as ManifestActions from './manifest.actions'; + +@Injectable() +export class ManifestEffects { + + constructor(private actions: Actions, private store: Store) { + + } + + submitRequest = createEffect( + () => this.actions.pipe( + ofType(ManifestActions.submitRequest), + pluck('request'), + mergeMap(request => combineLatest([ + of(request), + this.store.pipe( + select(selectManifestByName(request.manifestName)), + take(1) + ) + ])), + map(([request, manifest]) => { + + if (!manifest) { + return ManifestActions.queueRequest({ request }); + } + + const entry = manifest.entries.find(e => e.name === request.entryName); + + if (!entry) { + return ManifestActions.queueRequest({ request }); + } + + const method = request.method ? request.method : entry.methods[0]; + // TODO: validate method with allowed methods on manifests entry + const url = manifest.baseUrl + entry.path; + const options = { ...entry.options, ...request.options }; + const onSuccess = request.onSuccess ? request.onSuccess : []; + const onFailure = request.onFailure ? request.onFailure : []; + + const restAction = this.requestsByMethod(method); + + return restAction({ + request: { + url, + method, + options, + map: entry.map + }, + success: response => onSuccess.concat(ManifestActions.submitRequestSuccess({ manifest, request, response })), + failure: error => onFailure.concat(ManifestActions.submitRequestFailure({ manifest, request, error })) + }); + }) + )); + + dequeue = createEffect( + () => this.actions.pipe( + ofType( + ManifestActions.addManifest, + ManifestActions.setManifest, + ManifestActions.upsertManifest, + ManifestActions.addManifests, + ManifestActions.upsertManifests, + ManifestActions.updateManifest, + ManifestActions.updateManifests + ), + mergeMap(request => combineLatest([ + this.store.pipe( + select(selectManifestEntities), + take(1) + ), + this.store.pipe( + select(selectPendingRequests), + take(1) + ) + ])), + switchMap(([manifests, pendingRequests]) => { + const pendingRequestActions = []; + + pendingRequests.forEach(request => { + const manifest: Manifest = manifests[request.manifestName]; + if (manifest) { + const entry = manifest.entries.find(e => e.name === request.entryName); + if (entry) { + pendingRequestActions.push(ManifestActions.dequeueRequest({ request })); + } + } + }); + + return pendingRequestActions; + }) + ) + ); + + resubmitRequest = createEffect( + () => this.actions.pipe( + ofType(ManifestActions.dequeueRequest), + pluck('request'), + map(request => ManifestActions.submitRequest({ request })) + ) + ); + + private requestsByMethod = (method: RequestMethod) => { + switch (method) { + case 'OPTIONS': return RestActions.optionsRequest; + case 'POST': return RestActions.postRequest; + case 'PUT': return RestActions.putRequest; + case 'PATCH': return RestActions.patchRequest; + case 'DELETE': return RestActions.deleteRequest; + case 'GET': return RestActions.getRequest; + default: return; + } + }; + +} diff --git a/projects/wvr-elements/src/lib/core/manifest/manifest.reducers.ts b/projects/wvr-elements/src/lib/core/manifest/manifest.reducers.ts new file mode 100644 index 000000000..33655969b --- /dev/null +++ b/projects/wvr-elements/src/lib/core/manifest/manifest.reducers.ts @@ -0,0 +1,131 @@ +import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity'; +import { Action, createReducer, on } from '@ngrx/store'; +import { Manifest } from './manifest'; +import { ManifestEntryRequest } from './manifest-entry-request'; +import * as ManifestActions from './manifest.actions'; + +export interface State extends EntityState { + pendingRequests: Array; + currentRequest: ManifestEntryRequest; +} + +// tslint:disable-next-line:only-arrow-functions +export function selectManifestByName(manifest: Manifest): string { + return manifest.name; +} + +export const adapter: EntityAdapter = createEntityAdapter({ + selectId: selectManifestByName +}); + +export const initialState: State = adapter.getInitialState({ + pendingRequests: [], + currentRequest: undefined +}); + +const manifestReducer = createReducer( + initialState, + on(ManifestActions.addManifest, (state, { manifest }) => adapter.addOne(manifest, state)), + on(ManifestActions.setManifest, (state, { manifest }) => adapter.setOne(manifest, state)), + on(ManifestActions.upsertManifest, (state, { manifest }) => adapter.upsertOne(manifest, state)), + on(ManifestActions.addManifests, (state, { manifests }) => adapter.addMany(manifests, state)), + on(ManifestActions.upsertManifests, (state, { manifests }) => adapter.upsertMany(manifests, state)), + on(ManifestActions.updateManifest, (state, { update }) => adapter.updateOne(update, state)), + on(ManifestActions.updateManifests, (state, { updates }) => adapter.updateMany(updates, state)), + on(ManifestActions.mapManifest, (state, { entityMap }) => adapter.mapOne(entityMap, state)), + on(ManifestActions.mapManifests, (state, { entityMap }) => adapter.map(entityMap, state)), + on(ManifestActions.deleteManifest, (state, { id }) => adapter.removeOne(id, state)), + on(ManifestActions.deleteManifests, (state, { ids }) => adapter.removeMany(ids, state)), + on(ManifestActions.deleteManifestsByPredicate, (state, { predicate }) => adapter.removeMany(predicate, state)), + on(ManifestActions.loadManifests, (state, { manifests }) => adapter.setAll(manifests, state)), + on(ManifestActions.clearManifests, state => adapter.removeAll({ ...state, selectedManifestId: undefined })), + // tslint:disable-next-line:arrow-return-shorthand + on(ManifestActions.submitRequest, (state, { request }) => { + return { + ...state, + currentRequest: request + // tslint:disable-next-line:semicolon + } + }), + // tslint:disable-next-line:arrow-return-shorthand + on(ManifestActions.submitRequestSuccess, (state, { request, response, manifest }) => { + return adapter.updateOne({ + id: manifest.name, + changes: { + entries: manifest.entries.map(entry => { + if (entry.name === request.entryName) { + return { ...entry, request, response }; + } + + // tslint:disable-next-line:arrow-return-shorthand + return entry; + }) + } + }, { + ...state, + currentRequest: undefined + }); + }), + // tslint:disable-next-line:arrow-return-shorthand + on(ManifestActions.submitRequestFailure, (state, { request, error, manifest }) => { + return adapter.updateOne({ + id: manifest.name, + changes: { + entries: manifest.entries.map(entry => { + if (entry.name === request.entryName) { + return { ...entry, request, error }; + } + + return entry; + }) + } + }, { + ...state, + currentRequest: undefined + }); + }), + // tslint:disable-next-line:arrow-return-shorthand + on(ManifestActions.queueRequest, (state, { request }) => { + return { + ...state, + pendingRequests: state.pendingRequests.concat([{ ...request }]), + currentRequest: undefined + }; + }), + // tslint:disable-next-line:arrow-return-shorthand + on(ManifestActions.dequeueRequest, (state, { request }) => { + return { + ...state, + pendingRequests: state.pendingRequests.filter(r => r.manifestName !== request.manifestName && r.entryName !== request.entryName) + }; + }) +); + +// tslint:disable-next-line:typedef +export function reducer(state: State | undefined, action: Action) { + return manifestReducer(state, action); +} + +// get the selectors +const { + selectIds, + selectEntities, + selectAll, + selectTotal +} = adapter.getSelectors(); + +// select the array of manifest names +export const selectManifestNames = selectIds; + +// select the dictionary of manifest entities +export const selectManifestEntities = selectEntities; + +// select the array of manifests +export const selectAllManifests = selectAll; + +// select the total manifest count +export const selectManifestTotal = selectTotal; + +export const selectCurrentRequest = (state: State) => state.currentRequest; + +export const selectPendingRequests = (state: State) => state.pendingRequests; diff --git a/projects/wvr-elements/src/lib/core/manifest/manifest.service.ts b/projects/wvr-elements/src/lib/core/manifest/manifest.service.ts new file mode 100644 index 000000000..e69de29bb diff --git a/projects/wvr-elements/src/lib/core/manifest/manifest.ts b/projects/wvr-elements/src/lib/core/manifest/manifest.ts new file mode 100644 index 000000000..a3c5728b2 --- /dev/null +++ b/projects/wvr-elements/src/lib/core/manifest/manifest.ts @@ -0,0 +1,9 @@ +import { ManifestEntry } from './manifest-entry'; + +export interface Manifest { + name: string; // unique + description?: string; + baseUrl: string; + authorization?: any; + entries: Array; +} diff --git a/projects/wvr-elements/src/lib/core/mobile.service.spec.ts b/projects/wvr-elements/src/lib/core/mobile.service.spec.ts index a315b8161..11d355690 100644 --- a/projects/wvr-elements/src/lib/core/mobile.service.spec.ts +++ b/projects/wvr-elements/src/lib/core/mobile.service.spec.ts @@ -1,16 +1,22 @@ import { TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; import { MobileService } from './mobile.service'; +import { metaReducers, ROOT_REDUCER } from './store'; describe('MobileService', () => { let service: MobileService; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })] + }); service = TestBed.inject(MobileService); }); it('should be created', () => { - expect(service).toBeTruthy(); + expect(service) + .toBeTruthy(); }); }); diff --git a/projects/wvr-elements/src/lib/core/rest/request-method.ts b/projects/wvr-elements/src/lib/core/rest/request-method.ts new file mode 100644 index 000000000..c46639817 --- /dev/null +++ b/projects/wvr-elements/src/lib/core/rest/request-method.ts @@ -0,0 +1 @@ +export type RequestMethod = 'OPTIONS' | 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; diff --git a/projects/wvr-elements/src/lib/core/rest/request-options.ts b/projects/wvr-elements/src/lib/core/rest/request-options.ts new file mode 100644 index 000000000..169db53fe --- /dev/null +++ b/projects/wvr-elements/src/lib/core/rest/request-options.ts @@ -0,0 +1,8 @@ +export interface RequestOptions { + headers?: { [header: string]: string | Array; }; + observe?: 'body' | 'response' | 'events'; + params?: { [param: string]: string | Array; }; + reportProgress?: boolean; + responseType?: 'arraybuffer' | 'blob' | 'text' | 'json'; + withCredentials?: boolean; +} diff --git a/projects/wvr-elements/src/lib/core/rest/request.ts b/projects/wvr-elements/src/lib/core/rest/request.ts new file mode 100644 index 000000000..18cd892eb --- /dev/null +++ b/projects/wvr-elements/src/lib/core/rest/request.ts @@ -0,0 +1,12 @@ +import { RequestMethod } from './request-method'; +import { RequestOptions } from './request-options'; + +type map = (response: any) => any; + +export interface Request { + url: string; + method: RequestMethod; + body?: any; + options?: RequestOptions; + map?: map; +} diff --git a/projects/wvr-elements/src/lib/core/rest/rest.actions.ts b/projects/wvr-elements/src/lib/core/rest/rest.actions.ts new file mode 100644 index 000000000..84bcbd26d --- /dev/null +++ b/projects/wvr-elements/src/lib/core/rest/rest.actions.ts @@ -0,0 +1,106 @@ +import { HttpErrorResponse } from '@angular/common/http'; +import { Action, createAction, props } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { Request } from './request'; + +// TODO: remove to centralized export +type method = (request: Request) => Observable; +type success = (response: any) => Array; +type failure = (error: any) => Array; + +export const request = createAction( + '[REST] Request', + props<{ + request: Request, + method: method, + success: success, + failure: failure + }>() +); + +export const storeRequest = createAction( + '[REST] Store Request', + props<{ request: Request }>() +); + +export const requestSuccess = createAction( + '[REST] Request Success', + props<{ + response: any, + success: success + }>() +); + +export const requestFailure = createAction( + '[REST] Request Failure', + props<{ + error: HttpErrorResponse, + failure: failure + retry: Action + }>() +); + +export const optionsRequest = createAction( + '[REST] Options', + props<{ + request: Request, + success: success, + failure: failure + }>() +); + +export const getRequest = createAction( + '[REST] Get', + props<{ + request: Request, + success: success, + failure: failure + }>() +); + +export const postRequest = createAction( + '[REST] Post', + props<{ + request: Request, + success: success, + failure: failure + }>() +); + +export const putRequest = createAction( + '[REST] Put', + props<{ + request: Request, + success: success, + failure: failure + }>() +); + +export const patchRequest = createAction( + '[REST] Patch', + props<{ + request: Request, + success: success, + failure: failure + }>() +); + +export const deleteRequest = createAction( + '[REST] Delete', + props<{ + request: Request, + success: success, + failure: failure + }>() +); + +export const logResponse = createAction( + '[REST] Log', + props<{ + response: any + }>() +); + + + + diff --git a/projects/wvr-elements/src/lib/core/rest/rest.effects.ts b/projects/wvr-elements/src/lib/core/rest/rest.effects.ts new file mode 100644 index 000000000..73396c239 --- /dev/null +++ b/projects/wvr-elements/src/lib/core/rest/rest.effects.ts @@ -0,0 +1,139 @@ +import { Injectable } from '@angular/core'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; +import { Observable, of } from 'rxjs'; +import { catchError, map, mergeMap, switchMap } from 'rxjs/operators'; +import { Request } from './request'; +import * as RestActions from './rest.actions'; +import { RestService } from './rest.service'; + +@Injectable() +export class RestEffects { + + constructor( + private actions: Actions, + private rest: RestService + ) { + + } + + request = createEffect( + () => this.actions.pipe( + ofType(RestActions.request), + mergeMap(action => action.method(action.request) + .pipe( + map(response => RestActions.requestSuccess({ + response: action.request.map ? action.request.map(response) : response, + success: action.success + })), + catchError(error => of(RestActions.requestFailure({ + error, + failure: action.failure, + retry: action + }))) + )) + ) + ); + + requestSuccess = createEffect( + () => this.actions.pipe( + ofType(RestActions.requestSuccess), + switchMap(action => action.success(action.response)) + ) + ); + + requestFailure = createEffect( + () => this.actions.pipe( + ofType(RestActions.requestFailure), + // tslint:disable-next-line:arrow-return-shorthand + switchMap(action => { + // TODO: handle error, refresh token, retry + return action.failure(action.error); + }) + ) + ); + + optionsRequest = createEffect( + () => this.actions.pipe( + ofType(RestActions.optionsRequest), + map(action => RestActions.request({ + request: action.request, + method: this.options, + success: action.success, + failure: action.failure + })) + ) + ); + + getRequest = createEffect( + () => this.actions.pipe( + ofType(RestActions.getRequest), + map(action => RestActions.request({ + request: action.request, + method: this.get, + success: action.success, + failure: action.failure + })) + ) + ); + + postRequest = createEffect( + () => this.actions.pipe( + ofType(RestActions.postRequest), + map(action => RestActions.request({ + request: action.request, + method: this.post, + success: action.success, + failure: action.failure + })) + ) + ); + + putRequest = createEffect( + () => this.actions.pipe( + ofType(RestActions.putRequest), + map(action => RestActions.request({ + request: action.request, + method: this.put, + success: action.success, + failure: action.failure + })) + ) + ); + + patchRequest = createEffect( + () => this.actions.pipe( + ofType(RestActions.patchRequest), + map(action => RestActions.request({ + request: action.request, + method: this.patch, + success: action.success, + failure: action.failure + })) + ) + ); + + deleteRequest = createEffect( + () => this.actions.pipe( + ofType(RestActions.deleteRequest), + map(action => RestActions.request({ + request: action.request, + method: this.delete, + success: action.success, + failure: action.failure + })) + ) + ); + + private options = (request: Request): Observable => this.rest.options(request); + + private get = (request: Request): Observable => this.rest.get(request); + + private post = (request: Request): Observable => this.rest.post(request); + + private put = (request: Request): Observable => this.rest.put(request); + + private patch = (request: Request): Observable => this.rest.patch(request); + + private delete = (request: Request): Observable => this.rest.delete(request); + +} diff --git a/projects/wvr-elements/src/lib/core/rest/rest.reducers.ts b/projects/wvr-elements/src/lib/core/rest/rest.reducers.ts new file mode 100644 index 000000000..a1de22bea --- /dev/null +++ b/projects/wvr-elements/src/lib/core/rest/rest.reducers.ts @@ -0,0 +1,33 @@ +import { createReducer, on } from '@ngrx/store'; +import * as RestActions from './rest.actions'; +import { Request } from './request'; +​ +export interface State { + request: Request; + response: any; + error: any; +} +​ +export const initialState: State = { + request: undefined, + response: undefined, + error: undefined +}; +​ +export const reducer = createReducer( + initialState, + on(RestActions.request, (state, { request }) => ({ + ...state, + request, + response: undefined, + error: undefined + })), + on(RestActions.requestSuccess, (state, { response }) => ({ + ...state, + response + })), + on(RestActions.requestFailure, (state, { error }) => ({ + ...state, + error + })) +); diff --git a/projects/wvr-elements/src/lib/core/rest/rest.service.ts b/projects/wvr-elements/src/lib/core/rest/rest.service.ts new file mode 100644 index 000000000..bb80027e0 --- /dev/null +++ b/projects/wvr-elements/src/lib/core/rest/rest.service.ts @@ -0,0 +1,64 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { mergeMap, switchMap } from 'rxjs/operators'; +import { Request } from './request'; + +@Injectable({ + providedIn: 'root' +}) +export class RestService { + + constructor(private http: HttpClient) { + + } + + options(request: Request): Observable { + return this.processRequest(request, (url: string, options: any): any => this.http.options(url, options)); + } + + get(request: Request): Observable { + return this.processRequest(request, (url: string, options: any): any => this.http.get(url, options)); + } + + post(request: Request): Observable { + return this.processRequestWithData(request, (url: string, body: any, options: any): any => this.http.post(url, body, options)); + } + + put(request: Request): Observable { + return this.processRequestWithData(request, (url: string, body: any, options: any): any => this.http.put(url, body, options)); + } + + patch(request: Request): Observable { + return this.processRequestWithData(request, (url: string, body: any, options: any): any => this.http.patch(url, body, options)); + } + + delete(request: Request): Observable { + return this.processRequest(request, (url: string, options: any): any => this.http.delete(url, options)); + } + + private processRequest(request: Request, callback: (url: string, options: any) => Observable): Observable { + return this.preprocessOptions(request) + .pipe(mergeMap(options => callback(request.url, options))); + } + + // tslint:disable-next-line:max-line-length + private processRequestWithData(request: Request, callback: (url: string, body: any, options: any) => Observable): Observable { + return this.preprocessOptions(request) + .pipe(mergeMap(options => callback(request.url, request.body, options))); + } + + // tslint:disable-next-line:prefer-function-over-method + private preprocessOptions(request: Request): Observable { + const options = { ...request.options }; + + return new Observable(observer => { + if (options && options.withCredentials) { + // Access token work will go here + } + observer.next(options); + observer.complete(); + }); + } + +} diff --git a/projects/wvr-elements/src/lib/core/store.ts b/projects/wvr-elements/src/lib/core/store.ts new file mode 100644 index 000000000..5ec613b27 --- /dev/null +++ b/projects/wvr-elements/src/lib/core/store.ts @@ -0,0 +1,88 @@ +import { InjectionToken } from '@angular/core'; +import { ActionReducerMap, createFeatureSelector, createSelector, MetaReducer } from '@ngrx/store'; +import { Manifest } from './manifest/manifest'; +import { ManifestEntry } from './manifest/manifest-entry'; +import * as fromManifest from './manifest/manifest.reducers'; +import * as fromRest from './rest/rest.reducers'; + +export interface RootState { + rest: fromRest.State; + manifests: fromManifest.State; +} + +export const reducers: ActionReducerMap = { + rest: fromRest.reducer, + manifests: fromManifest.reducer +}; + +export const ROOT_REDUCER = new InjectionToken>('Root Reducer', { + factory: () => (reducers) +}); + +export const metaReducers: Array> = []; + +// rest selectors +export const selectRestState = createFeatureSelector('rest'); + +// manifest selectors +export const selectManifestState = createFeatureSelector('manifests'); + +export const selectManifestNames = createSelector( + selectManifestState, + fromManifest.selectManifestNames +); +export const selectManifestEntities = createSelector( + selectManifestState, + fromManifest.selectManifestEntities +); +export const selectAllManifests = createSelector( + selectManifestState, + fromManifest.selectAllManifests +); +export const selectManifestTotal = createSelector( + selectManifestState, + fromManifest.selectManifestTotal +); + +export const selectCurrentRequest = createSelector( + selectManifestState, + fromManifest.selectCurrentRequest +); + +export const selectPendingRequests = createSelector( + selectManifestState, + fromManifest.selectPendingRequests +); + +export const selectManifestByName = (manifestName: string) => createSelector( + selectManifestEntities, + manifestEntities => manifestEntities[manifestName] +); + +export const selectManifestEntryResponse = (manifestName: string, entryName: string) => createSelector( + selectManifestEntities, + manifestEntities => { + if (manifestEntities[manifestName]) { + const manifestEntry = findManifestEntry(manifestEntities[manifestName], entryName); + + return manifestEntry ? manifestEntry.response : undefined; + } + + return undefined; + } +); + +export const selectManifestEntryError = (manifestName: string, entryName: string) => createSelector( + selectManifestEntities, + manifestEntities => { + if (manifestEntities[manifestName]) { + const manifestEntry = findManifestEntry(manifestEntities[manifestName], entryName); + + return manifestEntry ? manifestEntry.error : undefined; + } + + return undefined; + } +); + +const findManifestEntry = (manifest: Manifest, entryName: string): ManifestEntry => manifest.entries.find(e => e.name === entryName); diff --git a/projects/wvr-elements/src/lib/core/template.service.spec.ts b/projects/wvr-elements/src/lib/core/template.service.spec.ts new file mode 100644 index 000000000..98412335d --- /dev/null +++ b/projects/wvr-elements/src/lib/core/template.service.spec.ts @@ -0,0 +1,54 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { WvrItWorksComponent } from '../wvr-it-works/wvr-it-works.component'; +import { WvrDataSelect } from './data-select'; +import { metaReducers, ROOT_REDUCER } from './store'; + +import { TemplateService } from './template.service'; + +describe('TemplateService', () => { + let service: TemplateService; + let component: WvrItWorksComponent; + let projectedContent: HTMLElement; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], + declarations: [WvrItWorksComponent], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }); + service = TestBed.inject(TemplateService); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(WvrItWorksComponent); + component = fixture.componentInstance; + projectedContent = document.createElement('wvr-template'); + projectedContent.innerHTML = ''; + + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(service) + .toBeTruthy(); + }); + + it('should compile a template', () => { + const testData = {test: 'foo'}; + const testSelect: WvrDataSelect = { + as: 'data', + entry: undefined, + manifest: undefined + }; + const elem = fixture.elementRef.nativeElement as HTMLElement; + elem.appendChild(projectedContent); + service.compile(testData, testSelect, elem, projectedContent); + expect(elem.querySelector('p').innerHTML) + .toEqual('foo'); + }); + +}); diff --git a/projects/wvr-elements/src/lib/core/template.service.ts b/projects/wvr-elements/src/lib/core/template.service.ts new file mode 100644 index 000000000..b355168be --- /dev/null +++ b/projects/wvr-elements/src/lib/core/template.service.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@angular/core'; +import * as Handlebars from 'handlebars'; +import { WvrBaseComponent } from '../shared/wvr-base.component'; +import { handlebarsHelpers } from './handlebars-helpers'; +import * as JSON5 from 'json5'; +import { WvrDataSelect } from './data-select'; + +@Injectable({ + providedIn: 'root' +}) +export class TemplateService { + + constructor() { + // Object.keys(handlebarsHelpers) + // .forEach(name => { + // const helper = handlebarsHelpers[name]; + // if (helper) { + // Handlebars.registerHelper(name, helper); + // } + // }); + } + + parseProjectedContent(component: WvrBaseComponent, elem: HTMLElement): void { + if (!component.hasWvrData()) { + return; + } + + setTimeout(() => { + const projectedContentElem = elem.querySelector('wvre-template') as HTMLElement; + if (!projectedContentElem) { + return; + } + const valueParsed = JSON5.parse(component.getWvrData()); + const wvrDataSelects: Array = Array.isArray(valueParsed) ? valueParsed : [valueParsed]; + + wvrDataSelects + .filter((s: WvrDataSelect) => !!s.manifest && !!s.entry && !!s.as) + .forEach((s: WvrDataSelect) => { + component.data[s.as].subscribe(d => this.compile(d, s, elem, projectedContentElem)); + }); + }); + } + + compile(d: {}, s: WvrDataSelect, elem: HTMLElement, projectedContentElem: HTMLElement): void { + const data = {}; + data[s.as] = d; + const compiledContent = Handlebars.compile(projectedContentElem.innerHTML)(data); + projectedContentElem.outerHTML = compiledContent + .replace('', ''); + } + +} diff --git a/projects/wvr-elements/src/lib/core/wvr-animation.service.spec.ts b/projects/wvr-elements/src/lib/core/wvr-animation.service.spec.ts index 53c113f12..4c23b8921 100644 --- a/projects/wvr-elements/src/lib/core/wvr-animation.service.spec.ts +++ b/projects/wvr-elements/src/lib/core/wvr-animation.service.spec.ts @@ -1,7 +1,9 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { ComponentFixture, TestBed, tick } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; import { WvrItWorksComponent } from '../wvr-it-works/wvr-it-works.component'; +import { metaReducers, ROOT_REDUCER } from './store'; import { WvrAnimationService } from './wvr-animation.service'; describe('WvrAnimationService', () => { @@ -13,7 +15,7 @@ describe('WvrAnimationService', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], declarations: [WvrItWorksComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/projects/wvr-elements/src/lib/core/wvr-animation.service.ts b/projects/wvr-elements/src/lib/core/wvr-animation.service.ts index df94f0a19..76f8916f7 100644 --- a/projects/wvr-elements/src/lib/core/wvr-animation.service.ts +++ b/projects/wvr-elements/src/lib/core/wvr-animation.service.ts @@ -74,7 +74,6 @@ export class WvrAnimationService { const from = animationConfig[animationName] ? animationConfig[animationName].from : wvrAnimationDefaults[animationName].from; - const a = this.selectAnimation(stateId, animationName, timing, to, from, animationRoot); if (a) { const animationFactory = this.builder.build(a); diff --git a/projects/wvr-elements/src/lib/shared/alignment.enum.ts b/projects/wvr-elements/src/lib/shared/alignment.enum.ts index 26db7f1e5..764cee490 100644 --- a/projects/wvr-elements/src/lib/shared/alignment.enum.ts +++ b/projects/wvr-elements/src/lib/shared/alignment.enum.ts @@ -1,5 +1,5 @@ /** - * This defines a set of constants used by the wvr-nav-list component in order to describe the position of the navigation list . + * This defines a set of constants used by the wvre-nav-list component in order to describe the position of the navigation list . */ export enum Alignment { LEFT = 'LEFT', RIGHT = 'RIGHT', CENTER = 'CENTER' diff --git a/projects/wvr-elements/src/lib/shared/pipes/default.pipe.spec.ts b/projects/wvr-elements/src/lib/shared/pipes/default.pipe.spec.ts new file mode 100644 index 000000000..d463788ac --- /dev/null +++ b/projects/wvr-elements/src/lib/shared/pipes/default.pipe.spec.ts @@ -0,0 +1,28 @@ +import { TestBed } from '@angular/core/testing'; +import { BrowserModule } from '@angular/platform-browser'; +import { DefaultPipe } from './default.pipe'; + +describe('DefaultPipe', () => { + let pipe: DefaultPipe; + + beforeEach(() => { + TestBed + .configureTestingModule({ + imports: [ + BrowserModule + ] + }); + pipe = new DefaultPipe(); + }); + + it('create an instance', () => { + expect(pipe) + .toBeTruthy(); + }); + + it('transforms undefined to default value', () => { + expect(pipe.transform(undefined, 'transformed')) + .toEqual('transformed'); + }); + +}); diff --git a/projects/wvr-elements/src/lib/shared/pipes/default.pipe.ts b/projects/wvr-elements/src/lib/shared/pipes/default.pipe.ts new file mode 100644 index 000000000..025caad88 --- /dev/null +++ b/projects/wvr-elements/src/lib/shared/pipes/default.pipe.ts @@ -0,0 +1,12 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'default' +}) +export class DefaultPipe implements PipeTransform { + + transform(value: string, defaultValue: string): string { + return value ? value : defaultValue; + } + +} diff --git a/projects/wvr-elements/src/lib/shared/pipes/safe.pipe.spec.ts b/projects/wvr-elements/src/lib/shared/pipes/safe.pipe.spec.ts new file mode 100644 index 000000000..577e32623 --- /dev/null +++ b/projects/wvr-elements/src/lib/shared/pipes/safe.pipe.spec.ts @@ -0,0 +1,24 @@ +import { inject, TestBed } from '@angular/core/testing'; +import { BrowserModule, DomSanitizer, SafeHtml } from '@angular/platform-browser'; +import { SafePipe } from './safe.pipe'; + +describe('SafePipe', () => { + + beforeEach(() => { + TestBed + .configureTestingModule({ + imports: [ + BrowserModule + ] + }); + }); + + it('transform html to SafeHtml', inject([DomSanitizer], (domSanitizer: DomSanitizer) => { + const pipe = new SafePipe(domSanitizer); + const safeHtml = pipe.transform('

test

', 'html'); + // tslint:disable-next-line:no-string-literal + expect(safeHtml['changingThisBreaksApplicationSecurity']) + .toEqual('

test

'); + })); + +}); diff --git a/projects/wvr-elements/src/lib/shared/pipes/safe.pipe.ts b/projects/wvr-elements/src/lib/shared/pipes/safe.pipe.ts new file mode 100644 index 000000000..5eb488c11 --- /dev/null +++ b/projects/wvr-elements/src/lib/shared/pipes/safe.pipe.ts @@ -0,0 +1,31 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { DomSanitizer, SafeHtml, SafeResourceUrl, SafeScript, SafeStyle, SafeUrl } from '@angular/platform-browser'; + +@Pipe({ + name: 'safe' +}) +export class SafePipe implements PipeTransform { + + constructor(protected _sanitizer: DomSanitizer) { + + } + + transform(value: string, type: 'html' | 'style' | 'script' | 'url' | 'resourceUrl') + : SafeHtml | SafeStyle | SafeScript | SafeUrl | SafeResourceUrl { + switch (type) { + case 'html': + return this._sanitizer.bypassSecurityTrustHtml(value); + case 'style': + return this._sanitizer.bypassSecurityTrustStyle(value); + case 'script': + return this._sanitizer.bypassSecurityTrustScript(value); + case 'url': + return this._sanitizer.bypassSecurityTrustUrl(value); + case 'resourceUrl': + return this._sanitizer.bypassSecurityTrustResourceUrl(value); + default: + throw new Error(`Unable to bypass security for invalid type: ${type}`); + } + } + +} diff --git a/projects/wvr-elements/src/lib/shared/utility/decorators.utilty.ts b/projects/wvr-elements/src/lib/shared/utility/decorators.utilty.ts index 3d0c6854a..bce284c1a 100644 --- a/projects/wvr-elements/src/lib/shared/utility/decorators.utilty.ts +++ b/projects/wvr-elements/src/lib/shared/utility/decorators.utilty.ts @@ -1,11 +1,16 @@ +import { MemoizedSelector, select } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { filter } from 'rxjs/operators'; + /** Used to delay the repeated execution of the decorated method until the specified time has elapsed. */ // tslint:disable-next-line:only-arrow-functions function debounce(delay = 300): MethodDecorator { - return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { + return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { + const timeoutKey = Symbol(); const original = descriptor.value; // tslint:disable-next-line:typedef - descriptor.value = function(...args) { + descriptor.value = function (...args) { // tslint:disable-next-line:no-invalid-this clearTimeout(this[timeoutKey]); // tslint:disable-next-line:no-invalid-this @@ -16,6 +21,31 @@ function debounce(delay = 300): MethodDecorator { }; } +export interface SelectOptions { + selector: MemoizedSelector; +} + +// tslint:disable-next-line:only-arrow-functions +function WvrSelect(option: SelectOptions): PropertyDecorator { + // tslint:disable-next-line:only-arrow-functions + // tslint:disable-next-line:typedef + return function(target: any, propertyKey: string) { + const getter = function(): Observable { + // tslint:disable-next-line:no-invalid-this + return this.store.pipe( + select(option.selector), + filter(r => !!r) + ); + }; + Object.defineProperty(target, propertyKey, { + get: getter, + enumerable: true, + configurable: false + }); + }; +} + export { - debounce + debounce, + WvrSelect }; diff --git a/projects/wvr-elements/src/lib/shared/utility/timing.utility.ts b/projects/wvr-elements/src/lib/shared/utility/timing.utility.ts new file mode 100644 index 000000000..662cdcba6 --- /dev/null +++ b/projects/wvr-elements/src/lib/shared/utility/timing.utility.ts @@ -0,0 +1,19 @@ +const wvrTimeout = (cb: Function, delay = 1): Function => { + let elapsedTime = 0; + const raf = requestAnimationFrame(deltaTime => { + elapsedTime += deltaTime; + if (elapsedTime > delay) { + cancelAnimationFrame(raf); + cb(); + } + }); + + return () => { + cancelAnimationFrame(raf); + }; + +}; + +export { + wvrTimeout +}; diff --git a/projects/wvr-elements/src/lib/shared/wvr-base.component.ts b/projects/wvr-elements/src/lib/shared/wvr-base.component.ts index 6ca454b3f..ddd660af9 100644 --- a/projects/wvr-elements/src/lib/shared/wvr-base.component.ts +++ b/projects/wvr-elements/src/lib/shared/wvr-base.component.ts @@ -1,19 +1,27 @@ import { AfterContentInit, Directive, ElementRef, EventEmitter, HostBinding, Injector, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; +import { select, Store } from '@ngrx/store'; import * as JSON5 from 'json5'; +import { Observable } from 'rxjs'; +import { filter } from 'rxjs/operators'; import { ComponentRegistryService } from '../core/component-registry.service'; +import { WvrDataSelect } from '../core/data-select'; +import * as ManifestActions from '../core/manifest/manifest.actions'; import { MobileService } from '../core/mobile.service'; +import { RootState, selectManifestEntryResponse } from '../core/store'; +import { TemplateService } from '../core/template.service'; import { WvrAnimationService } from '../core/wvr-animation.service'; @Directive() // tslint:disable-next-line:directive-class-suffix -export abstract class WvrBaseComponent implements AfterContentInit, OnInit, OnDestroy { +export abstract class WvrBaseComponent implements OnInit, OnDestroy { /** A generated unique identifier for this comonent. */ readonly id: number; - /** A statically accessible reference to the prefix used in deriving the HTML identifier. */ - static readonly HTML_ID_BASE = 'wvr-component'; + data: {[as: string]: Observable} = {}; + + @Input() private wvrData: string; /** A host binding used to ensure the presense of the `wvr-bootstrap` class. */ @HostBinding('class.wvr-bootstrap') wvrBootstrap = true; @@ -31,7 +39,7 @@ export abstract class WvrBaseComponent implements AfterContentInit, OnInit, OnDe /** A setter which parses a json string describing animation setting and stores the derived object in `_animationConfig`. */ @Input() set animateConfig(value: string) { - this._animationConfig = JSON5.parse(value); + this._animationConfig = JSON5.parse(value); } /** An identifier used to access the animation state for this component. */ @@ -58,8 +66,9 @@ export abstract class WvrBaseComponent implements AfterContentInit, OnInit, OnDe return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(agent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(agent.substr(0, 4))); } + protected readonly store: Store; /** A reference to the ComponentRegistryService */ - protected readonly componentRegistry: ComponentRegistryService; + protected readonly componentRegistry: ComponentRegistryService; /** A reference to the WvrAnimationService */ protected readonly _animationService: WvrAnimationService; @@ -73,6 +82,9 @@ export abstract class WvrBaseComponent implements AfterContentInit, OnInit, OnDe /** A reference to the MobileService */ private readonly mobileService: MobileService; + /** A reference to the MobileService */ + private readonly templateService: TemplateService; + /** A host bound accessor which applies the wvr-hidden class if both isMobileLayout and hiddenInMobile evaluate to true. */ @HostBinding('class.wvr-hidden') private get _hiddenInMobile(): boolean { return this.mobileService.isMobileLayout && this.hiddenInMobile; @@ -90,22 +102,31 @@ export abstract class WvrBaseComponent implements AfterContentInit, OnInit, OnDe this._domSanitizer = injector.get(DomSanitizer); this._eRef = injector.get(ElementRef); this.mobileService = injector.get(MobileService); + this.templateService = injector.get(TemplateService); + this.store = injector.get>(Store); this.id = this.componentRegistry.register(this); const element = (this._eRef.nativeElement as HTMLElement); const htmlIDAttrName = element.hasAttribute('id') ? 'wvr-id' : 'id'; - element.setAttribute(htmlIDAttrName, `${WvrBaseComponent.HTML_ID_BASE}-${this.id}`); + element.setAttribute(htmlIDAttrName, `${ComponentRegistryService.HTML_ID_BASE}-${this.id}`); } /** Used to setup this component for animating. */ ngOnInit(): void { - this.initializeAnimationRegistration(); + // this.processAnimations(); + this.processData(); + // this.initializeAnimationRegistration(); + this.templateService.parseProjectedContent(this, this._eRef.nativeElement); } + // TODO: fix this /** Used for post content initialization animation setup. */ - ngAfterContentInit(): void { - this.initializeAnimationElement(); - } + // ngAfterContentInit(): void { + // setTimeout(() => { + // this._animationService + // .initializeAnimationElement(this.animationStateId, this._animationConfig, this.animationRootElem); + // }, 1); + // } /** Handles the the unregistering of this component with the component registry. */ ngOnDestroy(): void { @@ -113,6 +134,7 @@ export abstract class WvrBaseComponent implements AfterContentInit, OnInit, OnDe } /** Plays the animation specified by the incoming animation trigger. */ + /* istanbul ignore next */ triggerAnimations(animationTriggerType: string): void { const animations: Array = Array.isArray(this._animationSettings[animationTriggerType]) ? this._animationSettings[animationTriggerType] @@ -127,6 +149,15 @@ export abstract class WvrBaseComponent implements AfterContentInit, OnInit, OnDe }); } + hasWvrData(): boolean { + return !!this.wvrData; + } + + getWvrData(): string { + return this.wvrData; + } + + /* istanbul ignore next */ private initializeAnimationRegistration(): void { const animationEvents = Object.keys(this._animationSettings); if (animationEvents.length) { @@ -142,6 +173,7 @@ export abstract class WvrBaseComponent implements AfterContentInit, OnInit, OnDe } } + /* istanbul ignore next */ private initializeAnimationElement(): void { setTimeout(() => { this._animationService @@ -150,8 +182,52 @@ export abstract class WvrBaseComponent implements AfterContentInit, OnInit, OnDe } /** Trigger's the animation specified by the incoming event. */ + /* istanbul ignore next */ private onEvent($event): void { this.triggerAnimations($event.type); } + /* istanbul ignore next */ + private processAnimations(): void { + const animationEvents = Object.keys(this._animationSettings); + if (animationEvents.length) { + if (this.animateId) { + this._animationService.registerAnimationTargets(this.animateId, this); + } + this.animationStateId = this._animationService.registerAnimationStates(); + animationEvents.forEach(eventName => { + if (eventName !== 'animationTrigger') { + (this._eRef.nativeElement as HTMLElement).addEventListener(eventName, this.onEvent.bind(this)); + } + }); + } + } + + /* istanbul ignore next */ + private processData(): void { + + if (!this.wvrData) { + return; + } + + const valueParsed = JSON5.parse(this.wvrData); + // tslint:disable-next-line:max-line-length + const wvrDataSelects: Array = Array.isArray(valueParsed) ? valueParsed : [valueParsed]; + + wvrDataSelects + .filter((s: WvrDataSelect) => !!s.manifest && !!s.entry && !!s.as) + .forEach(s => { + this.data[s.as] = this.store.pipe( + select(selectManifestEntryResponse(s.manifest, s.entry)), + filter(r => !!r) + ); + this.store.dispatch(ManifestActions.submitRequest({ + request: { + manifestName: s.manifest, + entryName: s.entry + } + })); + }); + } + } diff --git a/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.html b/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.html index 7104c7757..6691f347a 100644 --- a/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.html +++ b/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.html @@ -29,7 +29,7 @@ - + @@ -39,7 +39,7 @@ - + diff --git a/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.scss b/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.scss index d4786a299..515afbc12 100644 --- a/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.scss +++ b/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.scss @@ -58,6 +58,8 @@ --wvr-alert-link-dark-color-default : #040505; + font-family: var(--wvr-font-family-sans-serif); + .alert { position: relative; padding: .75rem 1.25rem; diff --git a/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.spec.ts b/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.spec.ts index 757f3a000..fa7d3d1c1 100644 --- a/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.spec.ts @@ -1,17 +1,37 @@ -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { Component, CUSTOM_ELEMENTS_SCHEMA, ViewChild } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../core/store'; import { WvrAlertComponent } from './wvr-alert.component'; +@Component({ + selector: 'wvr-alert-test-component', + // tslint:disable-next-line:component-max-inline-declarations + template: `` +}) +class WvrAlertHostComponent { + @ViewChild(WvrAlertComponent) alert: WvrAlertComponent; +} + describe('WvrAlertComponent', () => { let component: WvrAlertComponent; let fixture: ComponentFixture; + let hostComponent: WvrAlertHostComponent; + let hostFixture: ComponentFixture; + // tslint:disable-next-line: deprecation beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], - declarations: [WvrAlertComponent], + imports: [ + BrowserAnimationsModule, + StoreModule.forRoot(ROOT_REDUCER, { metaReducers }) + ], + declarations: [ + WvrAlertHostComponent, + WvrAlertComponent + ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) .compileComponents(); @@ -20,6 +40,11 @@ describe('WvrAlertComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(WvrAlertComponent); component = fixture.componentInstance; + + hostFixture = TestBed.createComponent(WvrAlertHostComponent); + hostComponent = hostFixture.componentInstance; + + hostFixture.detectChanges(); fixture.detectChanges(); }); @@ -50,4 +75,16 @@ describe('WvrAlertComponent', () => { expect(component.alertClosed) .toBeTrue(); }); + + it('should auto close when alert-type="self-closing"', done => { + + setTimeout(() => { + done(); + expect(hostComponent.alert + .alertClosed) + .toBeTrue(); + }, 100); + + }); + }); diff --git a/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.ts b/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.ts index c569c1b60..223507349 100644 --- a/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.ts +++ b/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.ts @@ -5,11 +5,11 @@ import { WvrBaseComponent } from '../shared/wvr-base.component'; * A message display with contextualized styling. */ @Component({ - selector: 'wvr-alert-element', + selector: 'wvr-alert-component', templateUrl: './wvr-alert.component.html', styleUrls: ['./wvr-alert.component.scss'] }) -export class WvrAlertComponent extends WvrBaseComponent implements AfterContentInit, OnInit { +export class WvrAlertComponent extends WvrBaseComponent implements OnInit { /** Used to define the class type of an alert component. */ @Input() alertClass: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark' = 'primary'; @@ -40,9 +40,11 @@ export class WvrAlertComponent extends WvrBaseComponent implements AfterContentI /** Initializes the closing timer for a self closing alert. */ ngOnInit(): void { - setTimeout(() => { - this.alertClosed = (this.alertType === 'self-closing') ? true : false; - }, this.closeTimer); + if (this.alertType === 'self-closing') { + setTimeout(() => { + this.alertClosed = true; + }, this.closeTimer); + } } } diff --git a/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.ud-examples.html b/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.ud-examples.html index 89bb92b05..a84c3b62e 100644 --- a/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.ud-examples.html +++ b/projects/wvr-elements/src/lib/wvr-alert/wvr-alert.component.ud-examples.html @@ -1,88 +1,88 @@ - The basic wvr-alert component with no customizations. + The basic wvre-alert component with no customizations. - - - + + - - - + + - - - + + - - - + + - The wvr-alert component with self closing feature based on the input closing timer. + The wvre-alert component with self closing feature based on the input closing timer. - - - + + - The wvr-alert component with custom content. + The wvre-alert component with custom content. - - +

- +

- +


- +

-
-
+ +
- - +

- +

- +


- +

-
-
+ +
diff --git a/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.html b/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.html index 6fdbe3585..f7b0602cd 100644 --- a/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.html +++ b/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.html @@ -62,5 +62,5 @@ - + diff --git a/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.scss b/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.scss index 6b09c2f9a..a93acdc4a 100644 --- a/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.scss +++ b/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.scss @@ -1070,7 +1070,7 @@ .btn { ::ng-deep { - wvr-icon { + wvre-icon { margin: 0px 5px 0px 0px; svg { vertical-align: middle; diff --git a/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.spec.ts b/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.spec.ts index 0e69448e3..173b2d1b4 100644 --- a/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.spec.ts @@ -1,6 +1,8 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../core/store'; import { WvrButtonComponent } from './wvr-button.component'; describe('WvrButtonComponent', () => { @@ -9,7 +11,7 @@ describe('WvrButtonComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], declarations: [WvrButtonComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.ts b/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.ts index db6971965..c87390fe3 100644 --- a/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.ts +++ b/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.ts @@ -1,8 +1,12 @@ import { Component, HostBinding, Injector, Input } from '@angular/core'; +import { Observable } from 'rxjs'; +import * as ManifestActions from '../core/manifest/manifest.actions'; +import * as rootStore from '../core/store'; +import { WvrSelect } from '../shared/utility/decorators.utilty'; import { WvrBaseComponent } from '../shared/wvr-base.component'; @Component({ - selector: 'wvr-button-element', + selector: 'wvr-button-component', templateUrl: './wvr-button.component.html', styleUrls: ['./wvr-button.component.scss'] }) @@ -362,6 +366,9 @@ export class WvrButtonComponent extends WvrBaseComponent { /** Allows for the override of button vertical align property */ @HostBinding('style.--wvr-btn-vertical-align') @Input() verticalAlign; + // tslint:disable-next-line:max-line-length + // @WvrSelect({ selector: rootStore.selectManifestEntryResponse('Directory App', 'All Sorted') }) private sampleTestResponse: Observable; + constructor(injector: Injector) { super(injector); } diff --git a/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.ud-examples.html b/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.ud-examples.html index d4937af5f..f0ae19980 100644 --- a/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.ud-examples.html +++ b/projects/wvr-elements/src/lib/wvr-button/wvr-button.component.ud-examples.html @@ -1,20 +1,20 @@ - The default wvr-button component with no customizations. + The default wvre-button component with no customizations. - - - + + + - The wvr-button component with customizations. + The wvre-button component with customizations. - - - + + - The wvr-button component with customizations. + The wvre-button component with customizations. - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - The wvr-button-outline component with customizations. + The wvre-button-outline component with customizations. - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - The wvr-button component with customizations. + The wvre-button component with customizations. - - -
+ + +
- - -
+ + +
- - - + + +
- The default wvr-button component with animations. + The default wvre-button component with animations. - - - + + + - - - - + + + + - - - + + diff --git a/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.html b/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.html new file mode 100644 index 000000000..61b06a69b --- /dev/null +++ b/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.html @@ -0,0 +1,110 @@ +
+ + + + + + + + +
+ + + + +
+ +
+
+ + + +
+ +
+
+ + + +
+ +
+
+ + + +
+ + +
+
+ + + +
+ +
+
+ + + +
+ +
+
+ + + + + + + + + + + + + + + + diff --git a/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.scss b/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.scss new file mode 100644 index 000000000..41fca6716 --- /dev/null +++ b/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.scss @@ -0,0 +1,19 @@ +@import "../shared/styles/wvr-variables.scss"; + +:host { + font-family: var(--wvr-font-family-sans-serif); + ::ng-deep { + + .list-group-flush { + border-top: 1px solid rgba(0, 0, 0, 0.125); + border-bottom: 1px solid rgba(0, 0, 0, 0.125); + } + + .bg-primary, + .bg-secondary, + .bg-danger, + .bg-dark { + color: var(--wvr-white); + } + } +} diff --git a/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.spec.ts b/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.spec.ts new file mode 100644 index 000000000..1d3796aa6 --- /dev/null +++ b/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.spec.ts @@ -0,0 +1,72 @@ +import { Component, CUSTOM_ELEMENTS_SCHEMA, ViewChild } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../core/store'; + +import { WvrCardComponent } from './wvr-card.component'; + +@Component({ + selector: 'wvr-card-test-component', + // tslint:disable-next-line:component-max-inline-declarations + template: ` + + + + + + + + + + +` +}) +class WvrCardHostComponent { + @ViewChild(WvrCardComponent) card: WvrCardComponent; +} + +describe('WvrCardComponent', () => { + let component: WvrCardComponent; + let fixture: ComponentFixture; + + let hostComponent: WvrCardHostComponent; + let hostFixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + BrowserAnimationsModule, + StoreModule.forRoot(ROOT_REDUCER, { metaReducers }) + ], + declarations: [ + WvrCardHostComponent, + WvrCardComponent + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(WvrCardComponent); + component = fixture.componentInstance; + + hostFixture = TestBed.createComponent(WvrCardHostComponent); + hostComponent = hostFixture.componentInstance; + + hostFixture.detectChanges(); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component) + .toBeTruthy(); + }); + + it('should create with projected content', () => { + expect(hostComponent.card) + .toBeTruthy(); + }); + +}); diff --git a/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.ts b/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.ts new file mode 100644 index 000000000..0d2d88f08 --- /dev/null +++ b/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.ts @@ -0,0 +1,183 @@ +import { AfterViewInit, Component, ElementRef, Injector, Input, ViewChild } from '@angular/core'; +import { WvrBaseComponent } from '../shared/wvr-base.component'; + +/** + * A component wrapper for the bootstrap card element. + */ +@Component({ + selector: 'wvr-card-component', + templateUrl: './wvr-card.component.html', + styleUrls: ['./wvr-card.component.scss'] +}) +export class WvrCardComponent extends WvrBaseComponent implements AfterViewInit { + + /** Indicates the presence of the card header in the projected content. */ + hasCardHeader: boolean; + + /** Indicates the presence of the card img in the projected content. */ + hasCardImg: boolean; + + /** Indicates the presence of the card tile in the projected content. */ + hasCardTitle: boolean; + + /** Indicates the presence of the card list top in the projected content. */ + hasCardListTop: boolean; + + /** Indicates the presence of the card list bottom in the projected content. */ + hasCardListBottom: boolean; + + /** Indicates the presence of the card links in the projected content. */ + hasCardLinkBody: boolean; + + /** Indicates the presence of the card buttons in the projected content. */ + hasCardButton: boolean; + + /** Indicates the presence of the card footer in the projected content. */ + hasCardFooter: boolean; + + /** The weaver card text center attribute */ + cardTextCenter: boolean; + + /** The weaver card footer text muted attribute */ + footerTextMuted: boolean; + + /** Allows for the override of the default 'wvre' sufix for psudo components. */ + @Input() selectorPrefix = 'wvre'; + + /** Toggles the centering of header and footer texts. */ + @Input() textCenter; + + /** Used to describe the type of card. */ + @Input() cardType: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark'; + + /** Used to describe the format of card. */ + @Input() panelFormat: 'solid' | 'outlined' | 'mixed'; + + /** Element reference to the root html elment in the template. */ + @ViewChild('animationRoot') rootElem: ElementRef; + + /** Convenience referece this components ElementReference's nativeElement. */ + private readonly elem: HTMLElement; + + /** + * The weaver card component constructor + */ + constructor(injector: Injector) { + super(injector); + this.elem = this._eRef.nativeElement as HTMLElement; + } + + /** Called after the view has been intialized. Handles the rendering of the projected content. */ + ngAfterViewInit(): void { + setTimeout(() => { + this.renderCard(); + this.renderCardHeader(); + this.renderCardImg(); + this.renderCardTitle(); + this.renderCardLinks(); + this.renderCardLists(); + this.renderCardButtons(); + this.renderCardFooter(); + }); + } + + isOutLined(cardType: string): boolean { + return this.cardType === cardType && + ((!this.panelFormat || this.panelFormat === 'mixed') || + this.panelFormat === 'outlined'); + } + + isSolid(cardType: string): boolean { + return this.cardType === cardType && this.panelFormat === 'solid'; + } + + isMixed(cardType: string): boolean { + return this.cardType === cardType && (!this.panelFormat || this.panelFormat === 'mixed'); + } + + /** Prepares the card for display */ + private renderCard(): void { + if (this.textCenter !== undefined && this.textCenter !== 'false') { + const wvrCardElem: HTMLElement = this.rootElem.nativeElement; + wvrCardElem.classList.add('text-center'); + } + } + + /** Prepares the card header for display, and sets it to the DOM */ + private renderCardHeader(): void { + const wvrCardHeaderElem = this.elem.querySelector(`${this.selectorPrefix}-card-header`); + + if (wvrCardHeaderElem) { + const classList: DOMTokenList = wvrCardHeaderElem.classList; + const headerText = wvrCardHeaderElem.innerHTML; + wvrCardHeaderElem.outerHTML = headerText; + // tslint:disable-next-line: no-unbound-method + classList.forEach(c => wvrCardHeaderElem.classList + .add); + this.hasCardHeader = true; + } + } + + /** Prepares the card footer for display, and sets it to the DOM */ + private renderCardFooter(): void { + const wvrCardFooterElem = this.elem.querySelector(`${this.selectorPrefix}-card-footer`); + if (wvrCardFooterElem) { + this.footerTextMuted = wvrCardFooterElem.hasAttribute('text-muted'); + const footerText = wvrCardFooterElem.innerHTML; + wvrCardFooterElem.outerHTML = footerText; + this.hasCardFooter = true; + } + } + + /** Prepares the card image for display, and sets it to the DOM */ + private renderCardImg(): void { + const wvrCardImgElem = this.elem.querySelector(`${this.selectorPrefix}-card-img`); + if (wvrCardImgElem) { + const imgSrc = wvrCardImgElem.getAttribute('src'); + wvrCardImgElem.outerHTML = `Card Image Cap`; + this.hasCardImg = true; + } + } + + /** Prepares the card title for display, and sets it to the DOM */ + private renderCardTitle(): void { + const wvrCardTitleElem = this.elem.querySelector(`${this.selectorPrefix}-card-title`); + if (wvrCardTitleElem) { + const titleText = wvrCardTitleElem.innerHTML; + wvrCardTitleElem.outerHTML = `${titleText}`; + this.hasCardTitle = true; + } + } + + /** Prepares the card links for display, and sets it to the DOM */ + private renderCardLinks(): void { + const wvrCardLinksElem = this.elem.querySelectorAll(`${this.selectorPrefix}-card-link`); + wvrCardLinksElem.forEach(link => { + const linkHref = link.getAttribute('href'); + const linkText = link.innerHTML; + link.outerHTML = `${linkText}`; + this.hasCardLinkBody = true; + }); + } + + /** Prepares the card list for display, and sets it to the DOM */ + private renderCardLists(): void { + const wvrCardListsTop = this.elem.querySelectorAll(`wvre-list[top], ${this.selectorPrefix}-list[top]`); + const wvrCardListsBottom = this.elem.querySelectorAll(`wvre-list[bottom], ${this.selectorPrefix}-list[bottom]`); + if (wvrCardListsTop.length) { + this.hasCardListTop = true; + } + if (wvrCardListsBottom.length) { + this.hasCardListBottom = true; + } + } + + /** Prepares the card buttons for display, and sets it to the DOM */ + private renderCardButtons(): void { + const wvrCardButton = this.elem.querySelectorAll(`${this.selectorPrefix}-button`); + if (wvrCardButton.length) { + this.hasCardButton = true; + } + } + +} diff --git a/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.ud-examples.html b/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.ud-examples.html new file mode 100644 index 000000000..db61b6f88 --- /dev/null +++ b/projects/wvr-elements/src/lib/wvr-card/wvr-card.component.ud-examples.html @@ -0,0 +1,55 @@ + + + The default wvre-card component with no customizations. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.html b/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.html index 943b7cbdb..38e509481 100644 --- a/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.html +++ b/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.html @@ -1,6 +1,6 @@
- - - + +
- +
diff --git a/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.scss b/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.scss index 615e9eb60..a02f188f8 100644 --- a/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.scss +++ b/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.scss @@ -22,7 +22,7 @@ display:none; } - wvr-button-element { + wvr-button-component { position: relative; z-index: 1001; } @@ -60,13 +60,13 @@ } ::ng-deep { - wvr-dropdown-menu { + wvre-dropdown-menu { display: var(--wvr-dropdown-menu-display); flex-direction: var(--wvr-dropdown-menu-flex-direction); height: auto; width: auto; - [wvr-dropdown-menu-item], - wvr-dropdown-menu-item { + [wvre-dropdown-menu-item], + wvre-dropdown-menu-item { margin: var(--wvr-dropdown-menu-item-margin) !important; } } diff --git a/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.spec.ts b/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.spec.ts index 94264c42e..0e89918a8 100644 --- a/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.spec.ts @@ -2,6 +2,8 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../core/store'; import { WvrDropdownComponent } from './wvr-dropdown.component'; describe('WvrDropdownComponent', () => { @@ -10,7 +12,7 @@ describe('WvrDropdownComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], declarations: [ WvrDropdownComponent, NgbDropdown @@ -398,4 +400,5 @@ describe('WvrDropdownComponent', () => { .toEqual('var(--wvr-btn-primary-hover-text-decoration)'); }); +// tslint:disable-next-line:max-file-line-count }); diff --git a/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.ts b/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.ts index c3179fd80..537ef9e0f 100644 --- a/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.ts +++ b/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.ts @@ -6,7 +6,7 @@ import { WvrBaseComponent } from '../shared/wvr-base.component'; * A dropdown with button and contextualized styling. */ @Component({ - selector: 'wvr-dropdown-element', + selector: 'wvr-dropdown-component', templateUrl: './wvr-dropdown.component.html', styleUrls: ['./wvr-dropdown.component.scss'] }) @@ -292,7 +292,7 @@ export class WvrDropdownComponent extends WvrBaseComponent { /** * Binds the input from `item-margin` to the css variable `--wvr-dropdown-item-margin`. - * This css variable is applied by both the `[wvr-dropdown-menu-item]` and the `wvr-dropdown-menu-item` + * This css variable is applied by both the `[wvre-dropdown-menu-item]` and the `wvre-dropdown-menu-item` * css rules to each item passed to the the dropdown menu. */ @HostBinding('style.--wvr-dropdown-menu-item-margin') @Input() menuItemMargin; @@ -391,7 +391,7 @@ export class WvrDropdownComponent extends WvrBaseComponent { /** * A handler method for the `document:click` event. * Closes the dropdown if `toggleOn` is set to `click` - * And the click occured off of the wvr-dropdown component. + * And the click occured off of the wvre-dropdown component. */ @HostListener('document:click', ['$event']) clickout($event): void { if (!this._eRef.nativeElement.contains($event.target)) { @@ -412,23 +412,27 @@ export class WvrDropdownComponent extends WvrBaseComponent { /** Handles the opening of the dropdown, and updating state. */ private openDropdown(): void { if (!this.isMobileLayout) { - document.querySelector('body') - .click(); - setTimeout(() => { - this.open = true; - this.dropdown.open(); - }, this._animationSpeedMili); + // document.querySelector('body') + // .click(); + // setTimeout(() => { + // this.open = true; + // this.dropdown.open(); + // }, this._animationSpeedMili); + + this.open = true; + this.dropdown.open(); } } /** Handles the closing of the dropdown, and updating state. */ private closeDropdown(): void { - this.closing = true; + // this.closing = true; this.open = false; - setTimeout(() => { - this.dropdown.close(); - this.closing = false; - }, this._animationSpeedMili); + this.dropdown.close(); + // setTimeout(() => { + // this.dropdown.close(); + // this.closing = false; + // }, this._animationSpeedMili); } // tslint:disable-next-line:max-file-line-count diff --git a/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.ud-examples.html b/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.ud-examples.html index d1a91110f..63c1734a1 100644 --- a/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.ud-examples.html +++ b/projects/wvr-elements/src/lib/wvr-dropdown/wvr-dropdown.component.ud-examples.html @@ -1,25 +1,25 @@ - The default wvr-dropdown component with no customizations. + The default wvre-dropdown component with no customizations. - - - - - - - - + + + + + + + + - The wvr-dropdown component with customizations. + The wvre-dropdown component with customizations. - - - - - -

-
-
- + + + +

+
+ + btn-text-decorationFocus="none" btn-text-decorationHover="none" btn-vertical-align="var(--wvr-btn-vertical-align)"> - - - - + + + +
    -
  • - -

    +
  • + +

-
-
+ +
- The wvr-dropdown component with customizations. + The wvre-dropdown component with customizations. - - - - - -

-
-
+ + + + + +

+
+
- - - - - -

-
-
+ + + + + +

+
+
- - - - - -

-
-
+ + + + + +

+
+
- - - - - -

-
-
+ + + + + +

+
+
- - - - - -

-
-
+ + + + + +

+
+
- - - - - -

-
-
+ + + + + +

+
+
- - - - - -

-
-
+ + + + + +

+
+
- - - - - -

-
-
+ + + + + +

+
+
- - - - - -

-
-
+ + + + + +

+
+
diff --git a/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.html b/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.html index e01ae9c91..cc6fb8ab1 100644 --- a/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.html +++ b/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.html @@ -2,7 +2,7 @@ diff --git a/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.spec.ts b/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.spec.ts index 0e577eb73..c458158f7 100644 --- a/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.spec.ts @@ -1,6 +1,8 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../core/store'; import { WvrFooterComponent } from './wvr-footer.component'; describe('WvrFooterComponent', () => { @@ -9,7 +11,7 @@ describe('WvrFooterComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], declarations: [WvrFooterComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.ts b/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.ts index e85223f5a..43780e6bb 100644 --- a/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.ts +++ b/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.ts @@ -6,7 +6,7 @@ import { WvrBaseComponent } from '../shared/wvr-base.component'; * A fullwidth footer component which attaches to the bottom of the document body. */ @Component({ - selector: 'wvr-footer-element', + selector: 'wvr-footer-component', templateUrl: './wvr-footer.component.html', styleUrls: ['./wvr-footer.component.scss'] }) diff --git a/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.ud-examples.html b/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.ud-examples.html index 02d75cd38..d15635251 100644 --- a/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.ud-examples.html +++ b/projects/wvr-elements/src/lib/wvr-footer/wvr-footer.component.ud-examples.html @@ -1,8 +1,8 @@ - The default wvr-footer component with no customizations. + The default wvre-footer component with no customizations. - + \ No newline at end of file diff --git a/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.html b/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.html index db89e8a36..f44539003 100644 --- a/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.html +++ b/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.html @@ -34,7 +34,7 @@
- + @@ -68,12 +68,12 @@
- +
- +
diff --git a/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.scss b/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.scss index 7e709920a..bd58fe374 100644 --- a/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.scss +++ b/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.scss @@ -76,7 +76,7 @@ tl-nav-li > a { white-space: nowrap !important; } - wvr-dropdown-element, + wvr-dropdown-component, .section-title { min-width: 200px; } diff --git a/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.spec.ts b/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.spec.ts index 61c79b676..d8cab2ae7 100644 --- a/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.spec.ts @@ -1,6 +1,10 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../core/store'; +import { APP_CONFIG } from '../shared/config/app-config'; +import { testAppConfig } from '../shared/config/test-app-config'; import { WvrHeaderComponent } from './wvr-header.component'; describe('WvrHeaderComponent', () => { @@ -9,10 +13,14 @@ describe('WvrHeaderComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ BrowserAnimationsModule ], + imports: [ BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers }) ], declarations: [ WvrHeaderComponent ], + providers: [{ + provide: APP_CONFIG, + useValue: testAppConfig + }], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) .compileComponents() @@ -75,8 +83,8 @@ describe('WvrHeaderComponent', () => { .toEqual(true); // creating mock childres - const wvrNavList = document.createElement('wvr-nav-list'); - const wvrNavLi = document.createElement('wvr-nav-li'); + const wvrNavList = document.createElement('wvre-nav-list'); + const wvrNavLi = document.createElement('wvre-nav-li'); // adding children to assert hidden attribute does not exist wvrNavList.appendChild(wvrNavLi); @@ -86,7 +94,7 @@ describe('WvrHeaderComponent', () => { .toEqual(false); // removing/replacing the children - const wvrNavLiElement = document.createElement('wvr-nav-li-element'); + const wvrNavLiElement = document.createElement('wvr-nav-li-component'); wvrNavList.replaceChild(wvrNavLiElement, wvrNavLi); fixture.detectChanges(); expect(bottomNavElement.hasAttribute('hidden')) diff --git a/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.ts b/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.ts index d7a97e327..37c4636df 100644 --- a/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.ts +++ b/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.ts @@ -1,11 +1,15 @@ -import { AfterContentChecked, Component, HostBinding, Injector, Input, OnInit } from '@angular/core'; +import { AfterContentChecked, Component, HostBinding, Inject, Injector, Input, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import * as rootStore from '../core/store'; +import { AppConfig, APP_CONFIG } from '../shared/config/app-config'; +import { WvrSelect } from '../shared/utility/decorators.utilty'; import { WvrBaseComponent } from '../shared/wvr-base.component'; /** * Intended to appear at the top of document and provides for branding, links and page title. */ @Component({ - selector: 'wvr-header-element', + selector: 'wvr-header-component', templateUrl: './wvr-header.component.html', styleUrls: ['./wvr-header.component.scss'] }) @@ -21,7 +25,7 @@ export class WvrHeaderComponent extends WvrBaseComponent implements OnInit, Afte @Input() headerTitleUrl: string; /** A resolvable URI to an image to be displayed as the logo. */ - @Input() logoSrc = 'assets/weaver-w.svg'; + @Input() logoSrc = `${this.appConfig.assetsUrl}/icons/custom/weaver-w.svg`; /** A resolvable URL to a location linkable from the logo. */ @Input() logoHref = '#logo'; @@ -77,31 +81,36 @@ export class WvrHeaderComponent extends WvrBaseComponent implements OnInit, Afte isBottomNavHidden = false; + mobileMenuClosed = true; + + @WvrSelect({ selector: rootStore.selectManifestEntryResponse('sample', 'one') }) private sampleTestResponse: Observable; + /** * The weaver header component constructor */ - constructor(injector: Injector) { + constructor(injector: Injector, + @Inject(APP_CONFIG) private readonly appConfig: AppConfig) { super(injector); } - mobileMenuClosed = true; - - toggleMobileMenu(): void { - this.mobileMenuClosed = !this.mobileMenuClosed; - } - ngOnInit(): void { super.ngOnInit(); this.checkBottomNavHasChildren(); + + // this.sampleTestResponse.subscribe(console.log); } ngAfterContentChecked(): void { this.checkBottomNavHasChildren(); } + toggleMobileMenu(): void { + this.mobileMenuClosed = !this.mobileMenuClosed; + } + /** Determines if the bottom nav list has children in order to display bottom nav section. */ private checkBottomNavHasChildren(): void { - const bottomNavListElement = (this._eRef.nativeElement as HTMLElement).querySelector('.bottom-nav wvr-nav-li, .bottom-nav wvr-nav-li-element'); + const bottomNavListElement = (this._eRef.nativeElement as HTMLElement).querySelector('.bottom-nav wvre-nav-li, .bottom-nav wvr-nav-li-component'); this.isBottomNavHidden = !(this.displayBottomNav === 'true' || (this.displayBottomNav === undefined && !!bottomNavListElement)); } diff --git a/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.ud-examples.html b/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.ud-examples.html index 2c4c8a94d..32f8e8382 100644 --- a/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.ud-examples.html +++ b/projects/wvr-elements/src/lib/wvr-header/wvr-header.component.ud-examples.html @@ -1,17 +1,17 @@ - The default wvr-header component with no customizations. + The default wvre-header component with no customizations. - + - The wvr-header component with all customizations. + The wvre-header component with all customizations. - + \ No newline at end of file diff --git a/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.html b/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.html index aa9d64cb0..62e33b480 100644 --- a/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.html +++ b/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.html @@ -1,2 +1,2 @@ - - + + diff --git a/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.scss b/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.scss index 2fe0f9f53..fb55a626b 100644 --- a/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.scss +++ b/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.scss @@ -5,7 +5,7 @@ --wvr-icon-color: inherit; --wvr-icon-size: inherit; - wvr-icon-wrapper { + wvre-icon-wrapper { color: var(--wvr-icon-color); display: inline-block; ::ng-deep { diff --git a/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.spec.ts b/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.spec.ts index 3ba807ef0..3eb5ebaaf 100644 --- a/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.spec.ts @@ -2,6 +2,8 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../core/store'; import { APP_CONFIG } from '../shared/config/app-config'; import { testAppConfig } from '../shared/config/test-app-config'; import { WvrIconComponent } from './wvr-icon.component'; @@ -12,7 +14,7 @@ describe('WvrIconComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, BrowserAnimationsModule], + imports: [HttpClientTestingModule, BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], declarations: [WvrIconComponent], providers: [{ provide: APP_CONFIG, diff --git a/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.ts b/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.ts index be39bf1b7..4378cdbc2 100644 --- a/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.ts +++ b/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.ts @@ -9,7 +9,7 @@ import { WvrBaseComponent } from '../shared/wvr-base.component'; * A reference to an Icon currently available in the configured assets. */ @Component({ - selector: 'wvr-icon-element', + selector: 'wvr-icon-component', templateUrl: './wvr-icon.component.html', styleUrls: ['./wvr-icon.component.scss'] }) diff --git a/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.ud-examples.html b/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.ud-examples.html index c99127cb1..c53a84277 100644 --- a/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.ud-examples.html +++ b/projects/wvr-elements/src/lib/wvr-icon/wvr-icon.component.ud-examples.html @@ -1,17 +1,17 @@ - The default wvr-icon component with no customizations. + The default wvre-icon component with no customizations. - + - The wvr-icon component with all customizations. + The wvre-icon component with all customizations. - + diff --git a/projects/wvr-elements/src/lib/wvr-it-works/wvr-it-works.component.spec.ts b/projects/wvr-elements/src/lib/wvr-it-works/wvr-it-works.component.spec.ts index 0e33e2697..669cf0f15 100644 --- a/projects/wvr-elements/src/lib/wvr-it-works/wvr-it-works.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-it-works/wvr-it-works.component.spec.ts @@ -1,6 +1,8 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../core/store'; import { WvrItWorksComponent } from './wvr-it-works.component'; describe('WvrItWorksComponent', () => { @@ -9,7 +11,7 @@ describe('WvrItWorksComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], declarations: [WvrItWorksComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) @@ -37,4 +39,10 @@ describe('WvrItWorksComponent', () => { expect(component.text) .toEqual('Weaver Components Work'); }); + + it('should set is mobileAgent', () => { + expect(component.isMobileAgent) + .toBeFalse(); + }); + }); diff --git a/projects/wvr-elements/src/lib/wvr-it-works/wvr-it-works.component.ts b/projects/wvr-elements/src/lib/wvr-it-works/wvr-it-works.component.ts index 41d45884a..f5dea5d99 100644 --- a/projects/wvr-elements/src/lib/wvr-it-works/wvr-it-works.component.ts +++ b/projects/wvr-elements/src/lib/wvr-it-works/wvr-it-works.component.ts @@ -1,11 +1,14 @@ import { Component, HostBinding, Injector, Input } from '@angular/core'; +import { Observable } from 'rxjs'; +import { selectManifestEntryResponse } from '../core/store'; +import { WvrSelect } from '../shared/utility/decorators.utilty'; import { WvrBaseComponent } from '../shared/wvr-base.component'; /** * Provides a simple component to prove the basic functionality of the Weaver Components. */ @Component({ - selector: 'wvr-it-works-element', + selector: 'wvr-it-works-component', templateUrl: './wvr-it-works.component.html', styleUrls: ['./wvr-it-works.component.scss'] }) diff --git a/projects/wvr-elements/src/lib/wvr-lib.module.ts b/projects/wvr-elements/src/lib/wvr-lib.module.ts index 5528368ad..8f8dbcad6 100644 --- a/projects/wvr-elements/src/lib/wvr-lib.module.ts +++ b/projects/wvr-elements/src/lib/wvr-lib.module.ts @@ -5,11 +5,22 @@ import { createCustomElement } from '@angular/elements'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { EffectsModule } from '@ngrx/effects'; +import { Store, StoreModule } from '@ngrx/store'; +import { StoreDevtoolsModule } from '@ngrx/store-devtools'; import { IconService } from './core/icon.service'; +import * as ManifestActions from './core/manifest/manifest.actions'; +import { ManifestEffects } from './core/manifest/manifest.effects'; import { MobileService } from './core/mobile.service'; -import { WvrAlertComponent } from './wvr-alert/wvr-alert.component'; +import { RestEffects } from './core/rest/rest.effects'; +import { metaReducers, RootState, ROOT_REDUCER } from './core/store'; +import { TemplateService } from './core/template.service'; import { WvrAnimationService } from './core/wvr-animation.service'; +import { DefaultPipe } from './shared/pipes/default.pipe'; +import { SafePipe } from './shared/pipes/safe.pipe'; +import { WvrAlertComponent } from './wvr-alert/wvr-alert.component'; import { WvrButtonComponent } from './wvr-button/wvr-button.component'; +import { WvrCardComponent } from './wvr-card/wvr-card.component'; import { WvrDropdownComponent } from './wvr-dropdown/wvr-dropdown.component'; import { WvrFooterComponent } from './wvr-footer/wvr-footer.component'; import { WvrHeaderComponent } from './wvr-header/wvr-header.component'; @@ -17,34 +28,40 @@ import { WvrIconComponent } from './wvr-icon/wvr-icon.component'; import { WvrItWorksComponent } from './wvr-it-works/wvr-it-works.component'; import { WvrListItemComponent } from './wvr-list/wvr-list-item/wvr-list-item.component'; import { WvrListComponent } from './wvr-list/wvr-list.component'; +import { WvrManifestEntryComponent } from './wvr-manifest/wvr-manifest-entry/wvr-manifest-entry.component'; +import { WvrManifestComponent } from './wvr-manifest/wvr-manifest.component'; import { WvrNavLiComponent } from './wvr-nav-list/wvr-nav-li/wvr-nav-li.component'; import { WvrNavListComponent } from './wvr-nav-list/wvr-nav-list.component'; -import { WvrTextComponent } from './wvr-text/wvr-text.component'; -import { WvrTabsComponent } from './wvr-tabs/wvr-tabs.component'; import { WvrTabComponent } from './wvr-tabs/wvr-tab/wvr-tab.component'; +import { WvrTabsComponent } from './wvr-tabs/wvr-tabs.component'; +import { WvrTextComponent } from './wvr-text/wvr-text.component'; /** This property contains a list of components and the selector tags. */ const elements = [ - { component: WvrAlertComponent, selector: 'wvr-alert'}, - { component: WvrButtonComponent, selector: 'wvr-button' }, - { component: WvrDropdownComponent, selector: 'wvr-dropdown' }, - { component: WvrFooterComponent, selector: 'wvr-footer' }, - { component: WvrHeaderComponent, selector: 'wvr-header' }, - { component: WvrIconComponent, selector: 'wvr-icon' }, - { component: WvrItWorksComponent, selector: 'wvr-it-works' }, - { component: WvrListComponent, selector: 'wvr-list' }, - { component: WvrListItemComponent, selector: 'wvr-list-item' }, - { component: WvrNavListComponent, selector: 'wvr-nav-list' }, - { component: WvrNavLiComponent, selector: 'wvr-nav-li' }, - { component: WvrTextComponent, selector: 'wvr-text' }, - { component: WvrTabsComponent, selector: 'wvr-tabs' }, - { component: WvrTabComponent, selector: 'wvr-tab' } + { component: WvrAlertComponent, selector: 'wvre-alert' }, + { component: WvrButtonComponent, selector: 'wvre-button' }, + { component: WvrCardComponent, selector: 'wvre-card' }, + { component: WvrDropdownComponent, selector: 'wvre-dropdown' }, + { component: WvrFooterComponent, selector: 'wvre-footer' }, + { component: WvrHeaderComponent, selector: 'wvre-header' }, + { component: WvrIconComponent, selector: 'wvre-icon' }, + { component: WvrItWorksComponent, selector: 'wvre-it-works' }, + { component: WvrListComponent, selector: 'wvre-list' }, + { component: WvrListItemComponent, selector: 'wvre-list-item' }, + { component: WvrNavListComponent, selector: 'wvre-nav-list' }, + { component: WvrManifestComponent, selector: 'wvre-manifest' }, + { component: WvrManifestEntryComponent, selector: 'wvre-manifest-entry' }, + { component: WvrNavLiComponent, selector: 'wvre-nav-li' }, + { component: WvrTextComponent, selector: 'wvre-text' }, + { component: WvrTabsComponent, selector: 'wvre-tabs' }, + { component: WvrTabComponent, selector: 'wvre-tab' } ]; /** This property contains a list of components classes. */ const components = [ WvrAlertComponent, WvrButtonComponent, + WvrCardComponent, WvrDropdownComponent, WvrFooterComponent, WvrHeaderComponent, @@ -55,28 +72,44 @@ const components = [ WvrNavListComponent, WvrNavLiComponent, WvrTextComponent, + WvrManifestComponent, + WvrManifestEntryComponent, WvrTabsComponent, WvrTabComponent ]; +const pipes = [ + SafePipe, + DefaultPipe +]; + /** The main module for the Weaver Elements library. */ @NgModule({ imports: [ BrowserAnimationsModule, BrowserModule, HttpClientModule, - NgbModule + NgbModule, + StoreModule.forRoot(ROOT_REDUCER, { metaReducers }), + EffectsModule.forRoot([ + RestEffects, + ManifestEffects + ]), + StoreDevtoolsModule.instrument() ], exports: [ - ...components + ...components, + ...pipes ], providers: [ IconService, MobileService, - WvrAnimationService + WvrAnimationService, + TemplateService ], declarations: [ - ...components + ...components, + ...pipes ], bootstrap: [], entryComponents: [ @@ -85,8 +118,7 @@ const components = [ schemas: [CUSTOM_ELEMENTS_SCHEMA] }) export class WvrLibModule { - - constructor(injector: Injector) { + constructor(injector: Injector, store: Store) { elements.forEach(element => { try { customElements.define(element.selector, createCustomElement(element.component, { injector })); diff --git a/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.html b/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.html index 0e9008ef1..616fd9806 100644 --- a/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.html +++ b/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.html @@ -1,73 +1,63 @@ - - - - - - - - - - - - - - +
  • + +
  • + +
    + {{description}} +
    +
    + +
    + +
  • + +
  • + + + +
  • + +
  • + + +
    +
    {{customContentItemHeading}}
    + {{customContentHeadingSmallText}} +
    +

    + +

    + {{customContentSmallText}} +
    + + - diff --git a/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.scss b/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.scss index ba18b2867..353ce5c6a 100644 --- a/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.scss +++ b/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.scss @@ -44,7 +44,7 @@ --wvr-list-group-item-dark-color: var(--wvr-list-group-item-dark-color-default); --wvr-list-group-item-dark-background-color: var(--wvr-list-group-item-dark-background-color-default); - wvr-list-wrapper { + wvre-list-wrapper { display: inline-block; } @@ -69,13 +69,13 @@ text-decoration: none; } - wvr-list-item:first-child > .list-group-item { + wvre-list-item:first-child > .list-group-item { margin-top: 0; border-top-right-radius: .25rem; border-top-left-radius: .25rem; } - wvr-list-item:last-child > .list-group-item { + wvre-list-item:last-child > .list-group-item { margin-bottom: 0; border-bottom-right-radius: .25rem; border-bottom-left-radius: .25rem; diff --git a/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.spec.ts b/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.spec.ts index ea6b89982..3bbc6aac2 100644 --- a/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.spec.ts @@ -1,6 +1,8 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../../core/store'; import { WvrListItemComponent } from './wvr-list-item.component'; describe('WvrListItemComponent', () => { @@ -9,7 +11,7 @@ describe('WvrListItemComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], declarations: [WvrListItemComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.ts b/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.ts index fa28dec76..cb67309d1 100644 --- a/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.ts +++ b/projects/wvr-elements/src/lib/wvr-list/wvr-list-item/wvr-list-item.component.ts @@ -1,21 +1,16 @@ -import { AfterContentInit, Component, ElementRef, Injector, Input, OnInit, ViewChild } from '@angular/core'; -import { SafeHtml } from '@angular/platform-browser'; +import { AfterViewInit, Component, Injector, Input, OnInit } from '@angular/core'; import { Theme } from '../../shared/theme.type'; import { WvrBaseComponent } from '../../shared/wvr-base.component'; -import { WvrListComponent } from '../wvr-list.component'; /** * A sub component to the WvrListComponent. */ @Component({ - selector: 'wvr-list-item-element', + selector: 'wvr-list-item-component', templateUrl: './wvr-list-item.component.html', styleUrls: ['./wvr-list-item.component.scss'] }) -export class WvrListItemComponent extends WvrBaseComponent implements OnInit, AfterContentInit { - - /** The WvrListComponent which contains this list item. */ - private parent: WvrListComponent; +export class WvrListItemComponent extends WvrBaseComponent implements OnInit, AfterViewInit { /** The type of list which contains this list item. */ listType: string; @@ -38,48 +33,27 @@ export class WvrListItemComponent extends WvrBaseComponent implements OnInit, Af /** A contructed identifier dervied from this comonents id and the prefix `wvr-li` */ htmlId = `wvr-li-${this.id}`; - /** A view child reference to the html template contianing the projected content. */ - @ViewChild('liWrapper') contentProjection: ElementRef; - - /** A getter for the html content contined withing the `contentProjection` template */ - get htmlContent(): string { - const elems = this.contentProjection.nativeElement.children; - - let htmlString = ''; - for (let i = 0; i < elems.length; i++) { - const elem = elems.item(i); - htmlString += elem.outerHTML; - } - - return htmlString; - } - constructor(injector: Injector) { super(injector); } /** Registers this list item with the parent list. */ ngOnInit(): void { + const parent = this._eRef.nativeElement.parentNode.parentNode.parentNode; + this.listType = parent.listType; + this.context = this.context ? this.context : parent.context ? parent.context : undefined; + } - const listElem: HTMLElement = (this._eRef.nativeElement as HTMLElement).closest('wvr-list'); - - if (listElem) { - this.parent = this.componentRegistry - .getComponentByElement(listElem) as WvrListComponent; - this.parent.addListItem(this); + ngAfterViewInit(): void { + // get the element's parent node + const parent = this._eRef.nativeElement.parentNode; - const listTypeAttribute = this.parent ? this.parent.listType : undefined; - this.listType = listTypeAttribute ? listTypeAttribute : 'unordered'; - const parentTheme = this.parent ? this.parent.context : undefined; - this.context = this.context ? - this.context : - parentTheme ? - parentTheme : - undefined; - } else { - console.warn('The wvr-list-item component must be contained within a wvr-list component.'); + // move all children out of the element + while (this._eRef.nativeElement.firstChild) { + parent.insertBefore(this._eRef.nativeElement.firstChild, this._eRef.nativeElement); } - + // remove the empty element + parent.removeChild(this._eRef.nativeElement); } } diff --git a/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.html b/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.html index 05e21343e..f4bebbd05 100644 --- a/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.html +++ b/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.html @@ -1,34 +1,40 @@ - - -
      + +
        +
      -
        +
          + +
        + +
          +
        -
        +
        +
        -
          +
            +
          -
          +
          +
          -
            +
              +
            -
            +
            +
            - +
            + - - - - + + - diff --git a/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.scss b/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.scss index 0de771392..e1a7b441b 100644 --- a/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.scss +++ b/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.scss @@ -3,24 +3,24 @@ :host { font-family: var(--wvr-font-family-sans-serif); - wvr-list-wrapper { + wvre-list-wrapper { display: block; overflow: hidden; } ::ng-deep { - .list-group-flush > wvr-list-item > wvr-list-item-wrapper > .list-group-item { + .list-group-flush > wvre-list-item > wvre-list-item-wrapper > .list-group-item { border-right: 0; border-left: 0; border-radius: 0; } - .list-group-flush > wvr-list-item:first-child > wvr-list-item-wrapper > .list-group-item { + .list-group-flush > wvre-list-item:first-child > wvre-list-item-wrapper > .list-group-item { border-top: 0; } - .list-group-flush > wvr-list-item:last-child > wvr-list-item-wrapper > .list-group-item { + .list-group-flush > wvre-list-item:last-child > wvre-list-item-wrapper > .list-group-item { border-bottom: 0; } diff --git a/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.spec.ts b/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.spec.ts index 4f576f307..8515a4bbc 100644 --- a/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.spec.ts @@ -1,6 +1,8 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../core/store'; import { WvrListComponent } from './wvr-list.component'; describe('WvrListComponent', () => { @@ -9,7 +11,7 @@ describe('WvrListComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], declarations: [WvrListComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.ts b/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.ts index e8dd68024..a53afd043 100644 --- a/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.ts +++ b/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.ts @@ -1,21 +1,16 @@ -import { AfterContentInit, AfterViewInit, Component, Injector, Input } from '@angular/core'; -import { SafeHtml } from '@angular/platform-browser'; +import { Component, Injector, Input } from '@angular/core'; import { Theme } from '../shared/theme.type'; import { WvrBaseComponent } from '../shared/wvr-base.component'; -import { WvrListItemComponent } from './wvr-list-item/wvr-list-item.component'; /** * A stylable list. */ @Component({ - selector: 'wvr-list-element', + selector: 'wvr-list-component', templateUrl: './wvr-list.component.html', styleUrls: ['./wvr-list.component.scss'] }) -export class WvrListComponent extends WvrBaseComponent implements AfterContentInit { - - /** All WvrListItemComponent contained within this list. */ - private readonly listItems: Array; +export class WvrListComponent extends WvrBaseComponent { /** Specifies the display format of this list. */ @Input() listType = 'unordered'; @@ -23,31 +18,8 @@ export class WvrListComponent extends WvrBaseComponent implements AfterContentIn /** Specifies the display style of this list. */ @Input() context: Theme; - /** The raw combined html for each list item. */ - private _htmlString: string; - - /** A SafeHtml represntation of the `_htmlString`. */ - get listItemsHtml(): SafeHtml { - return this._htmlString; - } - constructor(injector: Injector) { super(injector); - this.listItems = new Array(); - } - - /** Registers the incoming WvrListItemComponent as a child list item of this list. */ - addListItem(listItem: WvrListItemComponent): void { - this.listItems.push(listItem); - } - - /** Contstructs the `_htmlString` from the combined html content of each list item contained within this list. */ - ngAfterContentInit(): void { - setTimeout(() => { - this._htmlString = this.listItems - .map(li => li.htmlContent) - .join('\n'); - }, 0); } } diff --git a/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.ud-examples.html b/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.ud-examples.html index ef3a05942..8c41ca11f 100644 --- a/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.ud-examples.html +++ b/projects/wvr-elements/src/lib/wvr-list/wvr-list.component.ud-examples.html @@ -1,214 +1,214 @@ - Unordered wvr-list component. + Unordered wvre-list component. - - - - - - - - - - - + + + + + + + + + + + - Ordered wvr-list component. + Ordered wvre-list component. - - - - - - - - - - - + + + + + + + + + + + - Unstyled wvr-list component. + Unstyled wvre-list component. - - - - - - - - - - - + + + + + + + + + + + - Described wvr-list component. + Described wvre-list component. - - - - - - - - - - - + + + + + + + + + + + - Group wvr-list component. + Group wvre-list component. - - - - - - - - - - - + + + + + + + + + + + - Group - Flush wvr-list component. + Group - Flush wvre-list component. - - - - - - - - - - - + + + + + + + + + + + - Contextual Classes wvr-list component. + Contextual Classes wvre-list component. - - - + + + - + - - - + + + - + - - - + + + - + - - + + - Group - Custom Content wvr-list component. + Group - Custom Content wvre-list component. - - + - - - + + - - - + + - - - + + + - Unstyled wvr-list with expand/collapse animations. + Unstyled wvre-list with expand/collapse animations. - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/wvr-elements/src/lib/wvr-manifest/mapping-strategies.ts b/projects/wvr-elements/src/lib/wvr-manifest/mapping-strategies.ts new file mode 100644 index 000000000..28b8c8b38 --- /dev/null +++ b/projects/wvr-elements/src/lib/wvr-manifest/mapping-strategies.ts @@ -0,0 +1,19 @@ +import { MappingStrategy } from './mapping-strategy'; + +const none: MappingStrategy = { + map: data => data +}; + +const weaver: MappingStrategy = { + map: data => data.payload[Object.keys(data.payload)[0]] +}; + +const jsonparse: MappingStrategy = { + map: data => JSON.parse(data) +}; + +export { + none, + weaver, + jsonparse +}; diff --git a/projects/wvr-elements/src/lib/wvr-manifest/mapping-strategy.ts b/projects/wvr-elements/src/lib/wvr-manifest/mapping-strategy.ts new file mode 100644 index 000000000..3aa7c25f8 --- /dev/null +++ b/projects/wvr-elements/src/lib/wvr-manifest/mapping-strategy.ts @@ -0,0 +1,4 @@ +export interface MappingStrategy { + // tslint:disable-next-line:prefer-method-signature + map: (data: any) => {}; +} diff --git a/projects/wvr-elements/src/lib/wvr-manifest/wvr-manifest-entry/wvr-manifest-entry.component.spec.ts b/projects/wvr-elements/src/lib/wvr-manifest/wvr-manifest-entry/wvr-manifest-entry.component.spec.ts new file mode 100644 index 000000000..16bc95a42 --- /dev/null +++ b/projects/wvr-elements/src/lib/wvr-manifest/wvr-manifest-entry/wvr-manifest-entry.component.spec.ts @@ -0,0 +1,60 @@ +import { Component, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../../core/store'; +import { WvrManifestComponent } from '../wvr-manifest.component'; + +import { WvrManifestEntryComponent } from './wvr-manifest-entry.component'; + +@Component({ + selector: 'wvr-manifest-test-component', + // tslint:disable-next-line:component-max-inline-declarations + template: ` + + + + ` +}) +class WvrManifestTestComponent { + @ViewChild(WvrManifestComponent) manifest: WvrManifestComponent; +} + +describe('WvrManifestEntryComponent', () => { + let wvrManifestTestComponent: WvrManifestTestComponent; + let fixture: ComponentFixture; + + let childFixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], + declarations: [ WvrManifestTestComponent, WvrManifestComponent, WvrManifestEntryComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(WvrManifestTestComponent); + wvrManifestTestComponent = fixture.componentInstance; + fixture.detectChanges(); + + childFixture = TestBed.createComponent(WvrManifestEntryComponent); + + }); + + it('should create', () => { + expect(childFixture.componentInstance) + .toBeTruthy(); + }); + + it('should set parent', () => { + // tslint:disable-next-line:no-string-literal + const entries = wvrManifestTestComponent.manifest['manifestEntries']; + + // tslint:disable-next-line:no-string-literal + expect(entries[0]['parent']) + .toBeTruthy(); + }); + +}); diff --git a/projects/wvr-elements/src/lib/wvr-manifest/wvr-manifest-entry/wvr-manifest-entry.component.ts b/projects/wvr-elements/src/lib/wvr-manifest/wvr-manifest-entry/wvr-manifest-entry.component.ts new file mode 100644 index 000000000..40d58e9e7 --- /dev/null +++ b/projects/wvr-elements/src/lib/wvr-manifest/wvr-manifest-entry/wvr-manifest-entry.component.ts @@ -0,0 +1,40 @@ +import { Component, ElementRef, Input, OnInit } from '@angular/core'; +import { ComponentRegistryService } from '../../core/component-registry.service'; +import { WvrBaseComponent } from '../../shared/wvr-base.component'; +import { WvrManifestComponent } from '../wvr-manifest.component'; + +@Component({ + selector: 'wvr-manifest-entry-component', + template: '' +}) +export class WvrManifestEntryComponent implements OnInit { + + @Input() name; + + @Input() description; + + @Input() methods; + + @Input() path; + + @Input() options; + + @Input() mappingStrategy; + + private parent: WvrManifestComponent; + + // tslint:disable-next-line:no-empty + constructor(private readonly eRef: ElementRef, + private readonly componentRegistry: ComponentRegistryService) {} + + ngOnInit(): void { + const parentElem = this.eRef.nativeElement.closest('wvre-manifest, wvr-manifest-component'); + if (parentElem) { + this.parent = this.componentRegistry.getComponentByElement(parentElem as HTMLElement) as WvrManifestComponent; + this.parent.addEntry(this); + } else { + console.warn(`WvrManifestEntryComponent ${this.name} is not contained with a WvrManifestComponent`); + } + } + +} diff --git a/projects/wvr-elements/src/lib/wvr-manifest/wvr-manifest.component.spec.ts b/projects/wvr-elements/src/lib/wvr-manifest/wvr-manifest.component.spec.ts new file mode 100644 index 000000000..bb4ae2e1f --- /dev/null +++ b/projects/wvr-elements/src/lib/wvr-manifest/wvr-manifest.component.spec.ts @@ -0,0 +1,79 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { Component, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../core/store'; +import { WvrManifestEntryComponent } from './wvr-manifest-entry/wvr-manifest-entry.component'; + +import { WvrManifestComponent } from './wvr-manifest.component'; + +@Component({ + selector: 'wvr-manifest-test-component', + // tslint:disable-next-line:component-max-inline-declarations + template: ` + + + + + ` +}) +class WvrManifestHostComponent { + @ViewChild(WvrManifestComponent) manifest: WvrManifestComponent; +} + +describe('WvrManifestComponent', () => { + let hostComponent: WvrManifestHostComponent; + let hostFixture: ComponentFixture; + + let entryFixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + BrowserAnimationsModule, + StoreModule.forRoot(ROOT_REDUCER, { metaReducers }) + ], + declarations: [ + WvrManifestHostComponent, + WvrManifestComponent, + WvrManifestEntryComponent + ] + }) + .compileComponents(); + }); + + beforeEach(() => { + hostFixture = TestBed.createComponent(WvrManifestHostComponent); + hostComponent = hostFixture.componentInstance; + hostFixture.detectChanges(); + }); + + it('should create', () => { + expect(hostComponent.manifest) + .toBeTruthy(); + }); + + it('should add entry', () => { + + entryFixture = TestBed.createComponent(WvrManifestEntryComponent); + + // tslint:disable-next-line:no-string-literal + const entries = hostComponent.manifest['manifestEntries']; + const lengthBefore = entries.length; + + hostComponent.manifest.addEntry(entryFixture.componentInstance); + + expect(entries.length > lengthBefore) + .toBeTrue(); + + }); + + it('should create entries', () => { + // tslint:disable-next-line:no-string-literal + const entries = hostComponent.manifest['manifestEntries']; + expect(entries.length) + .toBeTruthy(); + }); + +}); diff --git a/projects/wvr-elements/src/lib/wvr-manifest/wvr-manifest.component.ts b/projects/wvr-elements/src/lib/wvr-manifest/wvr-manifest.component.ts new file mode 100644 index 000000000..e872c2919 --- /dev/null +++ b/projects/wvr-elements/src/lib/wvr-manifest/wvr-manifest.component.ts @@ -0,0 +1,87 @@ +import { Component, Injector, Input } from '@angular/core'; +import * as JSON5 from 'json5'; +import { Manifest } from '../core/manifest/manifest'; +import { ManifestEntry } from '../core/manifest/manifest-entry'; +import * as ManifestActions from '../core/manifest/manifest.actions'; +import { RequestMethod } from '../core/rest/request-method'; +import { debounce } from '../shared/utility'; +import { WvrBaseComponent } from '../shared/wvr-base.component'; +import * as mappingStrategies from './mapping-strategies'; +import { WvrManifestEntryComponent } from './wvr-manifest-entry/wvr-manifest-entry.component'; + +/** + * The WvrManifestComponent is used to express a potential remote data source. To be used + * with the `wvr-data` input. + */ +@Component({ + selector: 'wvr-manifest-component', + template: '' +}) +export class WvrManifestComponent extends WvrBaseComponent { + + /** The name by which this manifest can be referenced */ + @Input() private name: string; + + /** The base URL to be prepended to all paths expressed on ManifestEntries */ + @Input() private baseUrl: string; + + /** A human description of this manifes */ + @Input() private description: string; + + /** The strategy to be employed to unwrao response data */ + @Input() private mappingStrategy; + + /** A collection of the child WvrManifestEntryComponent */ + private readonly manifestEntries = new Array(); + + // tslint:disable-next-line:no-empty + constructor(private injector: Injector) { + super(injector); + } + + addEntry(manifestEntry: WvrManifestEntryComponent): void { + this.manifestEntries.push(manifestEntry); + this.buildEntries(); + } + + /** + * Converts this manifests WvrManifestEntryComponents into ManifestEntries + */ + @debounce() private buildEntries(): void { + + let ms = mappingStrategies[this.mappingStrategy] ? + mappingStrategies[this.mappingStrategy] : + mappingStrategies.none; + + const entries: Array = this.manifestEntries.map(e => { + const eMS = e.mappingStrategy; + ms = mappingStrategies[eMS] ? + mappingStrategies[eMS] : + ms; + + return { + name: e.name, + methods: e.methods ? e.methods + .split(',') as Array : [], + path: e.path, + description: e.description, + options: e.options ? JSON5.parse(e.options) : {}, + map: ms.map + }; + }); + + const manifest: Manifest = { + name: this.name, + description: this.description, + baseUrl: this.baseUrl, + entries, + authorization: undefined + }; + + this.store.dispatch(ManifestActions.addManifest({ + manifest + })); + + } + +} diff --git a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-li/wvr-nav-li.component.scss b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-li/wvr-nav-li.component.scss index 6a8fc53a4..51fe68d07 100644 --- a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-li/wvr-nav-li.component.scss +++ b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-li/wvr-nav-li.component.scss @@ -15,7 +15,7 @@ li.nav-item { height: var(--wvr-nav-li-height); min-width: var(--wvr-nav-li-width); cursor: var(--wvr-nav-li-cursor); - ::ng-deep wvr-dropdown { + ::ng-deep wvre-dropdown { display: flex; width: 100%; height: 100%; @@ -24,7 +24,7 @@ li.nav-item { [ngbdropdown], [ngdropdownanchor], - wvr-dropdown-btn, + wvre-dropdown-btn, .wvr-dropdown { width: 100%; height: 100%; @@ -32,7 +32,7 @@ li.nav-item { align-items: center; justify-content: center; } - wvr-dropdown-menu { + wvre-dropdown-menu { justify-self: self-start; } } diff --git a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-li/wvr-nav-li.component.spec.ts b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-li/wvr-nav-li.component.spec.ts index 9ce94d55a..ebea50af8 100644 --- a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-li/wvr-nav-li.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-li/wvr-nav-li.component.spec.ts @@ -1,6 +1,8 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../../core/store'; import { WvrNavLiComponent } from './wvr-nav-li.component'; describe('WvrNavLiComponent', () => { @@ -9,7 +11,7 @@ describe('WvrNavLiComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], declarations: [WvrNavLiComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-li/wvr-nav-li.component.ts b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-li/wvr-nav-li.component.ts index f7bc2619d..865f7bca8 100644 --- a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-li/wvr-nav-li.component.ts +++ b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-li/wvr-nav-li.component.ts @@ -2,10 +2,10 @@ import { Component, HostBinding, Injector, Input } from '@angular/core'; import { WvrBaseComponent } from '../../shared/wvr-base.component'; /** - * The WvrNavLi Component is the list element to be used with the wvr-nav-list element. + * The WvrNavLi Component is the list element to be used with the wvre-nav-list element. */ @Component({ - selector: 'wvr-nav-li-element', + selector: 'wvr-nav-li-component', templateUrl: './wvr-nav-li.component.html', styleUrls: ['./wvr-nav-li.component.scss'] }) diff --git a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.html b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.html index d979eeb78..bca04b7b9 100644 --- a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.html +++ b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.html @@ -1,4 +1,4 @@ diff --git a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.spec.ts b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.spec.ts index 0942b97a3..9d08d5d9e 100644 --- a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.spec.ts @@ -1,6 +1,8 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../core/store'; import { Alignment } from '../shared/alignment.enum'; import { WvrNavListComponent } from './wvr-nav-list.component'; @@ -10,7 +12,7 @@ describe('WvrNavListComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], declarations: [WvrNavListComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.ts b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.ts index 1f34005fd..f69da6e91 100644 --- a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.ts +++ b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.ts @@ -4,10 +4,10 @@ import { WvrBaseComponent } from '../shared/wvr-base.component'; /** * The WvrNavList Component presents a navigation list. - * Elements within this list must be wvr-nav-li elements and can be either links of action elements. + * Elements within this list must be wvre-nav-li elements and can be either links of action elements. */ @Component({ - selector: 'wvr-nav-list-element', + selector: 'wvr-nav-list-component', templateUrl: './wvr-nav-list.component.html', styleUrls: ['./wvr-nav-list.component.scss'] }) diff --git a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.ud-examples.html b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.ud-examples.html index 0b8b5f87f..1be9367c3 100644 --- a/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.ud-examples.html +++ b/projects/wvr-elements/src/lib/wvr-nav-list/wvr-nav-list.component.ud-examples.html @@ -1,18 +1,18 @@ - The wvr-nav-list component has the weaver-nav-li as a sub component. + The wvre-nav-list component has the weaver-nav-li as a sub component. - - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/projects/wvr-elements/src/lib/wvr-tabs/wvr-tab/wvr-tab.component.spec.ts b/projects/wvr-elements/src/lib/wvr-tabs/wvr-tab/wvr-tab.component.spec.ts index 9c07dc1ed..73e77d148 100644 --- a/projects/wvr-elements/src/lib/wvr-tabs/wvr-tab/wvr-tab.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-tabs/wvr-tab/wvr-tab.component.spec.ts @@ -1,6 +1,9 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../../core/store'; +import { WvrTabsComponent } from '../wvr-tabs.component'; import { WvrTabComponent } from './wvr-tab.component'; @@ -8,10 +11,13 @@ describe('WvrTabComponent', () => { let component: WvrTabComponent; let fixture: ComponentFixture; + let parentComponent: WvrTabsComponent; + let parentFixture: ComponentFixture; + beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], - declarations: [WvrTabComponent], + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], + declarations: [WvrTabsComponent, WvrTabComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) .compileComponents(); @@ -20,6 +26,12 @@ describe('WvrTabComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(WvrTabComponent); component = fixture.componentInstance; + + parentFixture = TestBed.createComponent(WvrTabsComponent); + parentComponent = parentFixture.componentInstance; + component.parent = parentComponent; + + parentComponent.addTab(component); fixture.detectChanges(); }); @@ -39,4 +51,21 @@ describe('WvrTabComponent', () => { expect(component.tabText.trim()) .toEqual(tabElem.innerHTML.trim()); }); + + it('should activate', () => { + + const secondFixture = TestBed.createComponent(WvrTabComponent); + const secondComponent = secondFixture.componentInstance; + secondComponent.parent = parentComponent; + + parentComponent.addTab(secondComponent); + + expect(secondComponent.active) + .toBeFalse(); + secondComponent.clickActivation(new MouseEvent('click')); + expect(secondComponent.active) + .toBeTrue(); + + }); + }); diff --git a/projects/wvr-elements/src/lib/wvr-tabs/wvr-tab/wvr-tab.component.ts b/projects/wvr-elements/src/lib/wvr-tabs/wvr-tab/wvr-tab.component.ts index aac54fdb9..b3271f442 100644 --- a/projects/wvr-elements/src/lib/wvr-tabs/wvr-tab/wvr-tab.component.ts +++ b/projects/wvr-elements/src/lib/wvr-tabs/wvr-tab/wvr-tab.component.ts @@ -7,7 +7,7 @@ import { WvrTabsComponent } from '../wvr-tabs.component'; * The constituent component in a tabs interface. */ @Component({ - selector: 'wvr-tab-element', + selector: 'wvr-tab-component', templateUrl: './wvr-tab.component.html', styleUrls: ['./wvr-tab.component.scss'] }) @@ -49,12 +49,12 @@ export class WvrTabComponent extends WvrBaseComponent implements AfterViewInit { /** Registers this tab with its parent as a child tab. */ ngAfterViewInit(): void { - const tabsElements: HTMLElement = (this._eRef.nativeElement as HTMLElement).closest('wvr-tabs, wvr-tabs-element'); + const tabsElements: HTMLElement = (this._eRef.nativeElement as HTMLElement).closest('wvre-tabs, wvr-tabs-component'); if (tabsElements) { this.parent = this.componentRegistry.getComponentByElement(tabsElements) as WvrTabsComponent; this.parent.addTab(this); } else { - console.warn('The wvr-tab component must be contained within a wvr-tabs component.'); + console.warn('The wvr-tab component must be contained within a wvre-tabs component.'); } } diff --git a/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.html b/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.html index fdebcba5c..427d5c6b1 100644 --- a/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.html +++ b/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.html @@ -5,5 +5,5 @@ - + diff --git a/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.spec.ts b/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.spec.ts index 13284190c..26c7d408f 100644 --- a/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.spec.ts @@ -1,6 +1,8 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../core/store'; import { WvrTabComponent } from './wvr-tab/wvr-tab.component'; import { WvrTabsComponent } from './wvr-tabs.component'; @@ -12,7 +14,7 @@ describe('WvrTabsComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], declarations: [WvrTabsComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.ts b/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.ts index 9ce03e7d1..9c73e26f4 100644 --- a/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.ts +++ b/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.ts @@ -7,7 +7,7 @@ import { WvrTabComponent } from './wvr-tab/wvr-tab.component'; * The principle component for a a tabbed presentation. */ @Component({ - selector: 'wvr-tabs-element', + selector: 'wvr-tabs-component', templateUrl: './wvr-tabs.component.html', styleUrls: ['./wvr-tabs.component.scss'] }) diff --git a/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.ud-examples.html b/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.ud-examples.html index 854e519c4..1779faed8 100644 --- a/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.ud-examples.html +++ b/projects/wvr-elements/src/lib/wvr-tabs/wvr-tabs.component.ud-examples.html @@ -1,17 +1,17 @@ - The default wvr-tabs component with no customizations. + The default wvre-tabs component with no customizations. - - -

            - -
            - -

            - -
            -
            + + +

            + +
            + +

            + +
            +
            diff --git a/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.html b/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.html index 527ead056..304882ba0 100644 --- a/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.html +++ b/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.html @@ -1,2 +1,2 @@ -{{value}} +{{value}} diff --git a/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.scss b/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.scss index fe7c74813..975b20c32 100644 --- a/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.scss +++ b/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.scss @@ -24,7 +24,7 @@ } -wvr-text-node { +wvre-text-node { //font rules font-family: var(--wvr-text-font-family); diff --git a/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.spec.ts b/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.spec.ts index 2ef730845..21bd65b8f 100644 --- a/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.spec.ts +++ b/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.spec.ts @@ -1,6 +1,8 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StoreModule } from '@ngrx/store'; +import { metaReducers, ROOT_REDUCER } from '../core/store'; import { WvrTextComponent } from './wvr-text.component'; describe('WvrTextComponent', () => { @@ -9,7 +11,7 @@ describe('WvrTextComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [BrowserAnimationsModule], + imports: [BrowserAnimationsModule, StoreModule.forRoot(ROOT_REDUCER, { metaReducers })], declarations: [WvrTextComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.ts b/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.ts index 6136a664f..124a44fa4 100644 --- a/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.ts +++ b/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.ts @@ -5,7 +5,7 @@ import { WvrBaseComponent } from '../shared/wvr-base.component'; * The Weaver Text Component allows for a node based textual entry. This will support i18n in the future. */ @Component({ - selector: 'wvr-text-element', + selector: 'wvr-text-component', templateUrl: './wvr-text.component.html', styleUrls: ['./wvr-text.component.scss'] }) @@ -14,53 +14,53 @@ export class WvrTextComponent extends WvrBaseComponent { /** The text value to be displayed in the text node. */ @Input() value: string; - /** Allows for the override of font-size property for wvr-text */ + /** Allows for the override of font-size property for wvre-text */ @HostBinding('style.--wvr-text-font-size') @Input() fontSize; - /** Allows for the override of font-family property for wvr-text */ + /** Allows for the override of font-family property for wvre-text */ @HostBinding('style.--wvr-text-font-family') @Input() fontFamily; - /** Allows for the override of font-style property for wvr-text */ + /** Allows for the override of font-style property for wvre-text */ @HostBinding('style.--wvr-text-font-style') @Input() fontStyle; - /** Allows for the override of font-variant property for wvr-text */ + /** Allows for the override of font-variant property for wvre-text */ @HostBinding('style.--wvr-text-font-variant') @Input() fontVariant; - /** Allows for the override of font-weight property for wvr-text */ + /** Allows for the override of font-weight property for wvre-text */ @HostBinding('style.--wvr-text-font-weight') @Input() fontWeight; - /** Allows for the override of font-stretch property for wvr-text */ + /** Allows for the override of font-stretch property for wvre-text */ @HostBinding('style.--wvr-text-font-stretch') @Input() fontStretch; - /** Allows for the override of line-height property for wvr-text */ + /** Allows for the override of line-height property for wvre-text */ @HostBinding('style.--wvr-text-line-height') @Input() lineHeight; // text rules - /** Allows for the override of color property for wvr-text */ + /** Allows for the override of color property for wvre-text */ @HostBinding('style.--wvr-text-color') @Input() textColor; - /** Allows for the override of text-align property for wvr-text */ + /** Allows for the override of text-align property for wvre-text */ @HostBinding('style.--wvr-text-text-align') @Input() textAlign; - /** Allows for the override of text-decoration property for wvr-text */ + /** Allows for the override of text-decoration property for wvre-text */ @HostBinding('style.--wvr-text-text-decoration') @Input() textDecoration; - /** Allows for the override of text-transform property for wvr-text */ + /** Allows for the override of text-transform property for wvre-text */ @HostBinding('style.--wvr-text-text-transform') @Input() textTransform; - /** Allows for the override of text-indent property for wvr-text */ + /** Allows for the override of text-indent property for wvre-text */ @HostBinding('style.--wvr-text-text-indent') @Input() textIndent; - /** Allows for the override of letter-spacing property for wvr-text */ + /** Allows for the override of letter-spacing property for wvre-text */ @HostBinding('style.--wvr-text-letter-spacing') @Input() letterSpacing; - /** Allows for the override of direction property for wvr-text */ + /** Allows for the override of direction property for wvre-text */ @HostBinding('style.--wvr-text-direction') @Input() textDirection; - /** Allows for the override of text-shadow property for wvr-text */ - @HostBinding('style. --wvr-text-text-shadow') @Input() textShadow; + /** Allows for the override of text-shadow property for wvre-text */ + @HostBinding('style.--wvr-text-text-shadow') @Input() textShadow; - /** Allows for the override of word-spacing property for wvr-text */ + /** Allows for the override of word-spacing property for wvre-text */ @HostBinding('style.--wvr-text-word-spacing') @Input() wordSpacing; constructor(injector: Injector) { diff --git a/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.ud-examples.html b/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.ud-examples.html index 759a967d5..304d7aca0 100644 --- a/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.ud-examples.html +++ b/projects/wvr-elements/src/lib/wvr-text/wvr-text.component.ud-examples.html @@ -1,8 +1,8 @@ - The default wvr-text component with no customization. + The default wvre-text component with no customization. - + \ No newline at end of file diff --git a/projects/wvr-elements/src/public-api.ts b/projects/wvr-elements/src/public-api.ts index 77585a52d..bdb344643 100644 --- a/projects/wvr-elements/src/public-api.ts +++ b/projects/wvr-elements/src/public-api.ts @@ -10,6 +10,7 @@ export * from './lib/core/mobile.service'; export * from './lib/core/icon.service'; export * from './lib/wvr-alert/wvr-alert.component'; export * from './lib/wvr-button/wvr-button.component'; +export * from './lib/wvr-card/wvr-card.component'; export * from './lib/wvr-dropdown/wvr-dropdown.component'; export * from './lib/wvr-footer/wvr-footer.component'; export * from './lib/wvr-header/wvr-header.component'; @@ -23,3 +24,4 @@ export * from './lib/wvr-nav-list/wvr-nav-list.component'; export * from './lib/wvr-tabs/wvr-tabs.component'; export * from './lib/wvr-tabs/wvr-tab/wvr-tab.component'; export * from './lib/wvr-text/wvr-text.component'; +export * from './lib/core/store'; diff --git a/scripts/build-docker.js b/scripts/build-docker.js new file mode 100644 index 000000000..96588a985 --- /dev/null +++ b/scripts/build-docker.js @@ -0,0 +1,24 @@ +#!/usr/bin/env node + +const shell = require('shelljs') +const package = require('../package.json'); + +const versionParts = package.version.split('.'); +const majorVersion = versionParts[0]; +const majorMinorVersion = `${versionParts[0]}.${versionParts[1]}`; +const tagBase = process.env.npm_package_config_DOCKER_SERVER.length ? + `${process.env.npm_package_config_DOCKER_SERVER}:` : + ''; +const dockerCmd = `docker build --build-arg MAJOR_VERSION=${majorVersion} --build-arg MAJOR_MINOR_VERSION=${majorMinorVersion} -t ${tagBase}${package.version} .`; + +shell.config.fatal = true; + +if (!shell.which('docker')) { + shell.echo('Sorry, this script requires docker to be installed'); + shell.exit(1); +} + +if (shell.exec(dockerCmd).code !== 0) { + shell.echo('Error: docker build failed'); + shell.exit(1); +} diff --git a/scripts/build-publish.js b/scripts/build-publish.js new file mode 100644 index 000000000..d9af4d51a --- /dev/null +++ b/scripts/build-publish.js @@ -0,0 +1,26 @@ +#!/usr/bin/env node + +const fs = require('fs-extra'); +const shell = require('shelljs') +const angularCli = require('@angular/cli'); + +const next = process.argv[2] ? + '--tag next' : + ''; + +angularCli.default({ + cliArgs: [ + 'b', + '--project=wvr-elements' + ], + inputStream: process.stdin, + outputStream: process.stdout + }).then(c => { + fs.copySync('scripts', "dist/wvr-elements/scripts"); + fs.copySync('.wvr-ud', "dist/wvr-elements/.wvr-ud"); + + shell.exec(`npm publish dist/wvr-elements/ ${next}`); + + shell.exit(); + + }); diff --git a/scripts/build-wvr-components-configuration.js b/scripts/build-wvr-components-configuration.js index e255d08bb..56355ddb7 100644 --- a/scripts/build-wvr-components-configuration.js +++ b/scripts/build-wvr-components-configuration.js @@ -1,3 +1,5 @@ +#!/usr/bin/env node + const dotEnv = require('dotenv-override-true'); const fs = require('fs-extra'); @@ -18,7 +20,6 @@ Object.keys(defaults.parsed) configTemplate = configTemplate.replace(`$${key}`, defaults.parsed[key]) }); -fs.writeFile('./dist/bundle/1x/config.json', configTemplate); -fs.writeFile('./dist/bundle/latest/config.json', configTemplate); +fs.writeFile('./dist/bundle/config.json', configTemplate); fs.writeFile('./static/weaver-components/docs/usage/config.json', configTemplate); fs.writeFile('./src/config.json', configTemplate); diff --git a/scripts/build-wvr-components-lighthouse-badges.js b/scripts/build-wvr-components-lighthouse-badges.js index bb4cdcfdf..3eeb7e55f 100644 --- a/scripts/build-wvr-components-lighthouse-badges.js +++ b/scripts/build-wvr-components-lighthouse-badges.js @@ -1,3 +1,5 @@ +#!/usr/bin/env node + const https = require('https'); const fs = require('fs-extra'); const glob = require("glob"); diff --git a/scripts/build-wvr-components-static.js b/scripts/build-wvr-components-static.js index 9e1ed7709..24aff704d 100644 --- a/scripts/build-wvr-components-static.js +++ b/scripts/build-wvr-components-static.js @@ -1,3 +1,5 @@ +#!/usr/bin/env node + const fs = require('fs-extra'); const staticPath = 'static'; const weaverComponentsPath = 'static/weaver-components'; diff --git a/scripts/build-wvr-components.js b/scripts/build-wvr-components.js index 75c19734e..2fd32890d 100644 --- a/scripts/build-wvr-components.js +++ b/scripts/build-wvr-components.js @@ -1,15 +1,11 @@ +#!/usr/bin/env node + const fs = require('fs-extra'); const concat = require('concat'); -const package = require('../package.json'); const cp = require('child_process'); -const majorVersion = package.version.split('.')[0]; const assetPath = 'dist/weaver-components'; -const basePath = 'dist/bundle'; -const dirName = `${majorVersion}x`; -const dirPath = `${basePath}/${dirName}`; -const latestPath = `${basePath}/latest`; - +const bundlePath = 'dist/bundle'; cp.fork(__dirname + '/build-wvr-config-template.js'); @@ -23,15 +19,13 @@ cp.fork(__dirname + '/build-wvr-config-template.js'); 'dist/weaver-components/main-es5.js' ]; - fs.ensureDir(dirPath); + fs.ensureDir(bundlePath); - await concat(files, `${dirPath}/weaver-components.js`); - fs.copy(`${dirPath}/weaver-components.js`, `${latestPath}/weaver-components.js`); - fs.copy(`${dirPath}/weaver-components.js`, 'dist/static/docs/usage/weaver-components.js'); + await concat(files, `${bundlePath}/weaver-components.js`); fs.copy('dist/weaver-components/assets', "dist/static/docs/usage/assets"); + fs.copy(`${assetPath}/assets`, `${bundlePath}/assets`); + + fs.copy('scripts', "dist/wvr-elements/scripts"); + fs.copy('.wvr-ud', "dist/wvr-elements/.wvr-ud"); - // to ensure static assets present in latest and x folders - fs.ensureDir(assetPath); - fs.copy(`${assetPath}/assets`, `${dirPath}/assets`); - fs.copy(`${assetPath}/assets`, `${latestPath}/assets`); })(); diff --git a/scripts/build-wvr-config-template.js b/scripts/build-wvr-config-template.js index e6cbecd2c..2229f4adc 100644 --- a/scripts/build-wvr-config-template.js +++ b/scripts/build-wvr-config-template.js @@ -1,3 +1,5 @@ +#!/usr/bin/env node + const fs = require('fs-extra'); const readline = require('readline'); diff --git a/scripts/build-wvr-ud.js b/scripts/build-wvr-ud.js old mode 100644 new mode 100755 index f60875260..c8c4b4d8a --- a/scripts/build-wvr-ud.js +++ b/scripts/build-wvr-ud.js @@ -1,4 +1,4 @@ -#!/usr/bin/env +#!/usr/bin/env node const process = require('process'); const fs = require('fs-extra'); @@ -16,6 +16,9 @@ const log = console.log; const startTime = new Date(); +const NM_WVR_UD_DIR = `${process.cwd()}/node_modules/@wvr/elements/.wvr-ud`; +const NM_UD_STATIC_ASSETS_DIR = `${NM_WVR_UD_DIR}/static-assets`; + const WVR_UD_DIR = `${process.cwd()}/.wvr-ud`; const WVR_UD_STATIC_ASSETS_DIR = `${WVR_UD_DIR}/static-assets`; const CONFIG = require(`${WVR_UD_DIR}/config.json`); @@ -113,36 +116,59 @@ additionalAssets.forEach(a=>{ log(` ${chalk.cyanBright('Total Additional Assets')}: ${chalk.blue(additionalAssets.length)}`); log(` ${chalk.green('Including Static Content:')}`); -let staticAssets = glob.sync(`${WVR_UD_STATIC_ASSETS_DIR}/**/*`, {}); -staticAssets.forEach(sa=>{ - log(` ${chalk.cyan('- Including: '+sa)}`); - let saPathParts = sa.split('/'); - let fileName = saPathParts[saPathParts.length-1]; - if(fs.lstatSync(sa).isDirectory()) { - copyFolderSync(sa, `${CONFIG.output}/${fileName}`); - } else { - fs.copyFileSync(sa, `${CONFIG.output}/${fileName}`); - } -}); -log(` ${chalk.cyanBright('Total content')}: ${chalk.blue(staticAssets.length)}`); -// Prepare Index -log(` ${chalk.green('Parsing Index Template:')}`); -log(` ${chalk.cyan('- Parsing: index-base.html')}`); -let content = fs.readFileSync(`${WVR_UD_DIR}/static-assets/index-base.html`, 'utf8'); -content = content.replace('{{EXAMPLE_MANIFEST}}', JSON.stringify(exampleManifest)); -content = content.replace(/{{PROJECT_NAME}}/g, `${packageJson.name} ${packageJson.version}`); -content = content.replace(/{{BASE_PATH}}/g, CONFIG.basePath); -fs.ensureDirSync(CONFIG.output); -fs.writeFileSync(`${CONFIG.output}/index.html`, content); -log(` ${chalk.cyanBright('Finished Parsing Index Template')}`); +const stlyeFiles = []; + +if(fs.existsSync(`${NM_UD_STATIC_ASSETS_DIR}/_styles.css`)) { + stlyeFiles.push(`${NM_UD_STATIC_ASSETS_DIR}/_styles.css`); +}; + +if(fs.existsSync(`${WVR_UD_STATIC_ASSETS_DIR}/_styles.css`)) { + stlyeFiles.push(`${WVR_UD_STATIC_ASSETS_DIR}/_styles.css`); +}; -const endTime = new Date(); -let timeDiff = endTime - startTime; //in ms -timeDiff /= 1000; +if(fs.existsSync(`${WVR_UD_STATIC_ASSETS_DIR}/overrides.css`)) { + stlyeFiles.push(`${WVR_UD_STATIC_ASSETS_DIR}/overrides.css`); +}; -log(` ${chalk.green('Usage Documentation Finished:')} ${chalk.blue(timeDiff+'s')}`); +concat(stlyeFiles, `${WVR_UD_STATIC_ASSETS_DIR}/styles.css`).finally(() => { + const staticAssets = glob.sync(`${WVR_UD_STATIC_ASSETS_DIR}/**/*`, {}); + staticAssets.forEach(sa=>{ + log(` ${chalk.cyan('- Including: '+sa)}`); + let saPathParts = sa.split('/'); + let fileName = saPathParts[saPathParts.length-1]; + if(fs.lstatSync(sa).isDirectory()) { + copyFolderSync(sa, `${CONFIG.output}/${fileName}`); + } else { + fs.copyFileSync(sa, `${CONFIG.output}/${fileName}`); + } + }); + + log(` ${chalk.cyanBright('Total content')}: ${chalk.blue(staticAssets.length)}`); + + // Prepare Index + log(` ${chalk.green('Parsing Index Template:')}`); + log(` ${chalk.cyan('- Parsing: index-base.html')}`); + const localIndexBase = `${WVR_UD_DIR}/static-assets/index-base.html`; + const nmIndexBase = `${NM_WVR_UD_DIR}/static-assets/index-base.html`; + let content = fs.existsSync(localIndexBase) ? + fs.readFileSync(localIndexBase, 'utf8') : + fs.readFileSync(nmIndexBase, 'utf8'); + content = content.replace('{{EXAMPLE_MANIFEST}}', JSON.stringify(exampleManifest)); + content = content.replace(/{{PROJECT_NAME}}/g, `${packageJson.name} ${packageJson.version}`); + content = content.replace(/{{BASE_PATH}}/g, CONFIG.basePath); + fs.ensureDirSync(CONFIG.output); + fs.writeFileSync(`${CONFIG.output}/index.html`, content); + log(` ${chalk.cyanBright('Finished Parsing Index Template')}`); + + const endTime = new Date(); + let timeDiff = endTime - startTime; //in ms + timeDiff /= 1000; + + log(` ${chalk.green('Usage Documentation Finished:')} ${chalk.blue(timeDiff+'s')}`); + +}); function copyFolderSync(from, to) { fs.mkdirSync(to); diff --git a/scripts/docker-push.js b/scripts/docker-push.js new file mode 100644 index 000000000..cf60a70af --- /dev/null +++ b/scripts/docker-push.js @@ -0,0 +1,22 @@ +#!/usr/bin/env node + +const shell = require('shelljs') +const package = require('../package.json'); + +const tagBase = process.env.npm_package_config_DOCKER_SERVER.length ? + `${process.env.npm_package_config_DOCKER_SERVER}:` : + ''; +const dockerCmd = `docker push ${tagBase}${package.version}`; + + +shell.config.fatal = true; + +if (!shell.which('docker')) { + shell.echo('This script requires docker to be installed'); + shell.exit(1); +} + +if (shell.exec(dockerCmd).code !== 0) { + shell.echo('Error: docker run failed'); + shell.exit(1); +} diff --git a/scripts/serve-dist.js b/scripts/serve-dist.js index fb5583759..076eb379c 100644 --- a/scripts/serve-dist.js +++ b/scripts/serve-dist.js @@ -1,13 +1,14 @@ +#!/usr/bin/env node + const fs = require('fs'); const process = require('process'); const StaticServer = require('static-server'); const fsPromises = fs.promises; const basePath = 'dist/bundle'; -const latestPath = `${basePath}/latest`; const server = new StaticServer({ - rootPath: 'dist/bundle/latest', + rootPath: 'dist/bundle', port: 8080, name: 'wvr-component-static-server', followSymlink: true, @@ -15,14 +16,14 @@ const server = new StaticServer({ server.start(function () { console.log('Server listening to', server.port); - fsPromises.copyFile('src/index.html', `${latestPath}/index.html`); + fsPromises.copyFile('src/index.html', `${basePath}/index.html`); }); process.on('exit', function () { - fs.unlink(`${latestPath}/index.html`, err => { + fs.unlink(`${basePath}/index.html`, err => { if (err) throw err; }); - fs.unlink(`${latestPath}/config.json`, err => { + fs.unlink(`${basePath}/config.json`, err => { if (err) throw err; }); console.log('Cleaning up'); diff --git a/scripts/start-docker.js b/scripts/start-docker.js new file mode 100644 index 000000000..d18aaaa40 --- /dev/null +++ b/scripts/start-docker.js @@ -0,0 +1,21 @@ +#!/usr/bin/env node + +const shell = require('shelljs') +const package = require('../package.json'); + +const tagBase = process.env.npm_package_config_DOCKER_SERVER.length ? + `${process.env.npm_package_config_DOCKER_SERVER}:` : + ''; +const dockerCmd = `docker run --env-file=defaults-dev-overrides.env -p 8080:80 -t ${tagBase}${package.version}`; + +shell.config.fatal = true; + +if (!shell.which('docker')) { + shell.echo('This script requires docker to be installed'); + shell.exit(1); +} + +if (shell.exec(dockerCmd).code !== 0) { + shell.echo('Error: docker run failed'); + shell.exit(1); +} diff --git a/src/assets/weaver-w.svg b/src/assets/weaver-w.svg deleted file mode 100644 index b90f3b7e3..000000000 --- a/src/assets/weaver-w.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - W - - diff --git a/src/index-docs.html b/src/index-docs.html index f25926352..7b9e01ca8 100644 --- a/src/index-docs.html +++ b/src/index-docs.html @@ -4,7 +4,7 @@ Weaver Components Documentation - +