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 = ``;
+ 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 @@