diff --git a/.angular-cli.json b/.angular-cli.json index 5dc34e7..e3260d1 100644 --- a/.angular-cli.json +++ b/.angular-cli.json @@ -18,8 +18,8 @@ "testTsconfig": "tsconfig.spec.json", "prefix": "app", "styles": [ - "../node_modules/bootstrap/dist/css/bootstrap.min.css", - "styles.css" + "my-theme.scss", + "styles.scss" ], "scripts": [], "environmentSource": "environments/environment.ts", diff --git a/e2e/app.e2e-spec.ts b/e2e/app.e2e-spec.ts index f6d3547..fcc4164 100644 --- a/e2e/app.e2e-spec.ts +++ b/e2e/app.e2e-spec.ts @@ -22,7 +22,7 @@ describe('d3-ng2-demo App', function () { it('should have one svg with initial dimensions ' + initialDragZoom2Dimensions.width + 'x' + initialDragZoom2Dimensions.height + ' in app-drag-zoom-2 component', () => { - let svg = page.getComponentElement('app-drag-zoom-2', 'svg'); + const svg = page.getComponentElement('app-drag-zoom-2', 'svg'); expect(svg.count()).toEqual(1, 'Incorrect number of elements'); expect(svg.first().getAttribute('width')).toEqual(initialDragZoom2Dimensions.width + '', 'Incorrect width.'); expect(svg.first().getAttribute('height')).toEqual(initialDragZoom2Dimensions.height + '', 'Incorrect height.'); @@ -32,13 +32,13 @@ describe('d3-ng2-demo App', function () { it('should resize the layout of the Drag & Zoom II visual to ' + resizedDragZoom2Dimensions.width + 'x' + resizedDragZoom2Dimensions.height + ' after pressing "large" button', () => { - let button = page.getLayoutButton('large'); + const button = page.getLayoutButton('large'); expect(button.count()).toEqual(1, 'Incorrect number of "large" buttons'); button.first().click(); - let svg = page.getComponentElement('app-drag-zoom-2', 'svg'); + const svg = page.getComponentElement('app-drag-zoom-2', 'svg'); expect(svg.count()).toEqual(1, 'Incorrect number of svg elements'); expect(svg.first().getAttribute('width')).toEqual(resizedDragZoom2Dimensions.width + '', 'Incorrect width.'); expect(svg.first().getAttribute('height')).toEqual(resizedDragZoom2Dimensions.height + '', 'Incorrect height.'); diff --git a/e2e/app.po.ts b/e2e/app.po.ts index 2e6fb0d..c5e491e 100644 --- a/e2e/app.po.ts +++ b/e2e/app.po.ts @@ -11,7 +11,7 @@ export class D3Ng2DemoPage { } getLayoutButton(buttonText: string) { - return element(by.tagName('app-wrapper-drag-zoom-2')).all(by.css('button.layout-' + buttonText)); + return element(by.tagName('app-wrapper-drag-zoom-2')).all(by.css('mat-button-toggle.layout-' + buttonText)); } } diff --git a/karma.conf.js b/karma.conf.js index 7a01a2a..31a81d8 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -34,6 +34,8 @@ module.exports = function (config) { flags: ['--no-sandbox'] } }, + browserDisconnectTimeout: 20000, + browserNoActivityTimeout: 20000, singleRun: false }); }; diff --git a/package-lock.json b/package-lock.json index 16bff97..c980132 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,6 +53,14 @@ "tslib": "1.8.1" } }, + "@angular/cdk": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-5.0.4.tgz", + "integrity": "sha512-6IKDOhDrfKdywTZNTWZbM1O8Q3cTi7uIOAyFC9sXitg0JUy2SHms0UY9FdW32BABIsZp692ofMCMdMPjHDjwwA==", + "requires": { + "tslib": "1.8.1" + } + }, "@angular/cli": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-1.6.4.tgz", @@ -163,6 +171,14 @@ "tslib": "1.8.1" } }, + "@angular/flex-layout": { + "version": "2.0.0-beta.12", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-2.0.0-beta.12.tgz", + "integrity": "sha512-QTOKZxehYTh8fj64V/pNVWNbfNtebSbssyMIXiGJuHTzfyF7GYdRmtjoR2pNpllycz3rE5NYX77EB140Y6BCnw==", + "requires": { + "tslib": "1.8.1" + } + }, "@angular/forms": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-5.2.0.tgz", @@ -185,6 +201,14 @@ "integrity": "sha512-Yj/y5XrBBXbtwnZ6HJOBl/0XS/JBvVlHb1UWw5MIrsc81k7/eFiRAIKzmGpAba+Sw0G0dO22ABZ4yJjAMsML6A==", "dev": true }, + "@angular/material": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-5.0.4.tgz", + "integrity": "sha512-xrGYPHOlNXpPkventLAvZyTjfCo2A8ZfdyJEHNR4LhFWIjw3ilwb1ihNv4dy/qG56g8L4AwQ2cONxQ0YyZfcWg==", + "requires": { + "tslib": "1.8.1" + } + }, "@angular/platform-browser": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-5.2.0.tgz", @@ -1215,11 +1239,6 @@ "hoek": "2.16.3" } }, - "bootstrap": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.3.7.tgz", - "integrity": "sha1-WjiTlFSfIzMIdaOxUGVldPip63E=" - }, "brace-expansion": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", diff --git a/package.json b/package.json index 152c8e1..7fd951e 100644 --- a/package.json +++ b/package.json @@ -28,15 +28,17 @@ }, "dependencies": { "@angular/animations": "^5.1.0", + "@angular/cdk": "^5.0.4", "@angular/common": "^5.1.0", "@angular/compiler": "^5.1.0", "@angular/core": "^5.1.0", + "@angular/flex-layout": "^2.0.0-beta.12", "@angular/forms": "^5.1.0", "@angular/http": "^5.1.0", + "@angular/material": "^5.0.4", "@angular/platform-browser": "^5.1.0", "@angular/platform-browser-dynamic": "^5.1.0", "@angular/router": "^5.1.0", - "bootstrap": "3.3.7", "core-js": "^2.4.1", "d3-ng2-service": "1.23.3", "rxjs": "^5.5.2", diff --git a/src/_extra-colors.scss b/src/_extra-colors.scss new file mode 100644 index 0000000..b2609b5 --- /dev/null +++ b/src/_extra-colors.scss @@ -0,0 +1,187 @@ +@import '~@angular/material/theming'; + +@mixin extra-color-classes($theme) { + $background: map-get($theme, background); + $foreground: map-get($theme, foreground); + $accent: map-get($theme, accent); + $primary: map-get($theme, primary); + $warn: map-get($theme, warn); + + .extra-code { + color: mat-color($accent, darker); + background-color: mat-color($accent, lighter); + } + + .extra-text { + color: mat-color($foreground, text); + } + + .extra-divider { + color: mat-color($foreground, divider); + } + + .extra-hint-text { + color: mat-color($foreground, hint-text); + } + + .extra-secondary-text { + color: mat-color($foreground, secondary-text); + } + + // Primary Colors --------------------------------------------------- + + .extra-primary-default { + color: mat-color($primary, default); + } + + .extra-primary-lighter { + color: mat-color($primary, lighter); + } + + .extra-primary-darker { + color: mat-color($primary, darker); + } + + .extra-primary-default-bg { + background-color: mat-color($primary, default-contrast); + } + + .extra-primary-lighter-bg { + background-color: mat-color($primary, lighter-contrast); + } + + .extra-primary-darker-bg { + background-color: mat-color($primary, darker-contrast); + } + + // Primary Colors Inverted --------------------------------------------------- + + .extra-inverted-primary-default { + color: mat-color($primary, default-contrast); + } + + .extra-inverted-primary-lighter { + color: mat-color($primary, lighter-contrast); + } + + .extra-inverted-primary-darker { + color: mat-color($primary, darker-contrast); + } + + .extra-inverted-primary-default-bg { + background-color: mat-color($primary); + } + + .extra-inverted-primary-lighter-bg { + background-color: mat-color($primary, lighter); + } + + .extra-inverted-primary-darker-bg { + background-color: mat-color($primary, darker); + } + + // Accent Colors --------------------------------------------------- + + .extra-accent-default { + color: mat-color($accent, default); + } + + .extra-accent-lighter { + color: mat-color($accent, lighter); + } + + .extra-accent-darker { + color: mat-color($accent, darker); + } + + .extra-accent-default-bg { + background-color: mat-color($accent, default-contrast); + } + + .extra-accent-lighter-bg { + background-color: mat-color($accent, lighter-contrast); + } + + .extra-accent-darker-bg { + background-color: mat-color($accent, darker-contrast); + } + + // Accent Colors Inverted --------------------------------------------------- + + .extra-inverted-accent-default { + color: mat-color($accent, default-contrast); + } + + .extra-inverted-accent-lighter { + color: mat-color($accent, lighter-contrast); + } + + .extra-inverted-accent-darker { + color: mat-color($accent, darker-contrast); + } + + .extra-inverted-accent-default-bg { + background-color: mat-color($accent); + } + + .extra-inverted-accent-lighter-bg { + background-color: mat-color($accent, lighter); + } + + .extra-inverted-accent-darker-bg { + background-color: mat-color($accent, darker); + } + + // Warn Colors --------------------------------------------------- + + .extra-warn-default { + color: mat-color($warn, default); + } + + .extra-warn-lighter { + color: mat-color($warn, lighter); + } + + .extra-warn-darker { + color: mat-color($warn, darker); + } + + .extra-warn-default-bg { + background-color: mat-color($warn, default-contrast); + } + + .extra-warn-lighter-bg { + background-color: mat-color($warn, lighter-contrast); + } + + .extra-warn-darker-bg { + background-color: mat-color($warn, darker-contrast); + } + + // Warn Colors Inverted --------------------------------------------------- + + .extra-inverted-warn-default { + color: mat-color($warn, default-contrast); + } + + .extra-inverted-warn-lighter { + color: mat-color($warn, lighter-contrast); + } + + .extra-inverted-warn-darker { + color: mat-color($warn, darker-contrast); + } + + .extra-inverted-warn-default-bg { + background-color: mat-color($warn); + } + + .extra-inverted-warn-lighter-bg { + background-color: mat-color($warn, lighter); + } + + .extra-inverted-warn-darker-bg { + background-color: mat-color($warn, darker); + } + +} diff --git a/src/app/app.component.css b/src/app/app.component.css index b402a32..38b6cc8 100644 --- a/src/app/app.component.css +++ b/src/app/app.component.css @@ -1,18 +1,15 @@ -div.jumbotron { - background-color: rgb(230, 230, 255); +.spacer { + flex: 1 1 auto; } -div.jumbotron p { - font-size: 14px; +.splash-image { + max-height: 400px; } -div.jumbotron img { - max-height: 400px; - margin-left: auto; - margin-right: auto; +.text-justify p { + text-align: justify; } -footer { - background-color: #222; - color: #eee; +.text-center p { + text-align: center; } diff --git a/src/app/app.component.html b/src/app/app.component.html index 19f08ee..123e8f8 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,113 +1,124 @@ - + + + {{title}} + + + + + Github + + + -
+ -
-

Reusable Visual Power?

-

Fuse D3 and Angular in TypeScript (A Demo)

-
-
-
-

Perfect Storm

-
-

- Ever wondered, how to build modern web apps with reusable, interactive data visualization components? -

-

- The latest incarnation of D3 is getting you excited about new destinations. -

-

- Angular 2 arrived on the tarmac and has since been rolled into a stable release management cycle. -

-

- TypeScript has become the fuel that lets you develop complex solutions at scale. -

-

- Integrating all three, however, seems as daunting as taking off in the perfect storm. -

-
-
-
- - -
-
-

Take Flight

-
-

- This Angular demo app and its source code repository are intended to get you started. Rather than trying to be exhaustive - or writing a lengthy blog post, a select few well-known D3 demo scripts have been adapted to highlight key considerations. -

-

- Use an Angular Service to inject the power of D3 version 4 into a project. -

-

- Explore how D3 visualizations can be implemented as Angular components in TypeScript 2. -

-

- See how a visualization component can change in response to input data changes. -

-

- Find ways to unit test your D3 components with Karma/Jasmine and check out a worked example end-to-end test using Protractor. -

-
+
+
+

Reusable Visual Power?

+

+ Fuse D3 and Angular in TypeScript (A Demo) +

+
+ +
+

Perfect Storm

+
+

+ Ever wondered, how to build modern web apps with reusable, interactive data visualization components? +

+

+ The latest incarnation of + D3 is getting you excited about new destinations. +

+

+ Angular 2 arrived on the tarmac and has since been rolled into a stable release management cycle. +

+

+ TypeScript has become the fuel that lets you develop complex solutions at scale. +

+

+ Integrating all three, however, seems as daunting as taking off in the perfect storm. +

-
+
+ +
-
+
+

Take Flight

+
+

+ This Angular demo app and its source code repository are intended to get you started. Rather than trying to be exhaustive + or writing a lengthy blog post, a select few well-known D3 demo scripts have been adapted to highlight key considerations. +

+

+ Use an Angular Service to inject the power of D3 version 4 into a project. +

+

+ Explore how D3 visualizations can be implemented as Angular components in TypeScript 2. +

+

+ See how a visualization component can change in response to input data changes. +

+

+ Find ways to unit test your D3 components with Karma/Jasmine and check out a worked example end-to-end test using Protractor. +

+
+
+
- - - - + diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index a5c7c2a..d2fd5c8 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -4,6 +4,17 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { Component } from '@angular/core'; import { By } from '@angular/platform-browser'; +import { + MatButtonModule, + MatListModule, + MatSidenavModule, + MatToolbarModule +} from '@angular/material'; + +import { + FlexLayoutModule +} from '@angular/flex-layout'; + import { AppComponent } from './app.component'; let fixture: ComponentFixture; @@ -41,6 +52,13 @@ describe('App: D3Ng2Demo', () => { MockChild1Component, MockChild2Component, MockChild3Component + ], + imports: [ + MatButtonModule, + FlexLayoutModule, + MatListModule, + MatSidenavModule, + MatToolbarModule ] }); @@ -62,10 +80,10 @@ describe('App: D3Ng2Demo', () => { }); it(`should render title 'D3 Angular Demo' in a span of class 'navbar-brand'`, () => { - let debugEl = fixture.debugElement.query(By.css('span.navbar-brand')); + const debugEl = fixture.debugElement.query(By.css('span.toolbar-title')); expect(debugEl).not.toBeNull('Missing.'); if (debugEl) { - let nativeEl: HTMLSpanElement = debugEl.nativeElement; + const nativeEl: HTMLSpanElement = debugEl.nativeElement; expect(nativeEl.textContent).toContain('D3 Angular Demo'); } }); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 25221ea..f3ea930 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,5 +1,15 @@ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { + MatButtonModule, + MatButtonToggleModule, + MatCardModule, + MatListModule, + MatSidenavModule, + MatToolbarModule +} from '@angular/material'; import { D3Service } from 'd3-ng2-service'; @@ -22,7 +32,15 @@ import { WrapperVoronoiSpirals3Component } from './d3-demos/wrapper-voronoi-spir WrapperVoronoiSpirals3Component ], imports: [ - BrowserModule + BrowserModule, + BrowserAnimationsModule, + FlexLayoutModule, + MatButtonModule, + MatButtonToggleModule, + MatCardModule, + MatListModule, + MatSidenavModule, + MatToolbarModule ], providers: [ D3Service diff --git a/src/app/d3-demos/wrapper-brush-zoom-2/wrapper-brush-zoom-2.component.css b/src/app/d3-demos/wrapper-brush-zoom-2/wrapper-brush-zoom-2.component.css index 7c5a8e7..3f14dc6 100644 --- a/src/app/d3-demos/wrapper-brush-zoom-2/wrapper-brush-zoom-2.component.css +++ b/src/app/d3-demos/wrapper-brush-zoom-2/wrapper-brush-zoom-2.component.css @@ -1,19 +1,5 @@ -div.container { - background-color: rgb(245, 250, 245); - margin-top: 10px; - margin-bottom: 10px; - border-radius: 10px; -} - -div.col { - padding-top: 10px; - padding-bottom: 5px; -} - -h3 { - border-bottom: 1px solid #337ab7; - padding-top: 10px; - margin-bottom: 20px; +.mat-card { + margin: 8px; } div.visualization { @@ -27,3 +13,6 @@ div.visualization { overflow-x: auto; } +.text-justify { + text-align: justify; +} diff --git a/src/app/d3-demos/wrapper-brush-zoom-2/wrapper-brush-zoom-2.component.html b/src/app/d3-demos/wrapper-brush-zoom-2/wrapper-brush-zoom-2.component.html index 9e41f7e..4a2d6d1 100644 --- a/src/app/d3-demos/wrapper-brush-zoom-2/wrapper-brush-zoom-2.component.html +++ b/src/app/d3-demos/wrapper-brush-zoom-2/wrapper-brush-zoom-2.component.html @@ -1,60 +1,58 @@ -
-
-
-

Brush & Zoom II

-
-
-
-
-

- Demonstrate how to: -

-
    -
  • - build an Angular component which uses a D3 service to render to <svg>, and -
  • -
  • - use D3 version 4 in TypeScript to draw a scatter plot with brush and zoom/pan event handling. -
  • -
-
-
-
-
-

Reference

-

- This example is based on Mike Bostock's Brush & Zoom II Block. The original source with its supporting explanation - can be found by following the link below. -

-

- Source: Brush & Zoom II Block. -

-
-
-

D3 Angular Implementation

-

- The implementation is a straightforward Angular component without any inputs. Its template is an <svg> element with fixed dimensions. The component renders the visualization using the d3 instance provided - by the injected D3Service. -

-

- The D3 component can be embedded into a parent component using the following tag: -

-

- <app-brush-zoom-2></app-brush-zoom-2>. -

-

- The source code for the D3 component can be found at: -

-

- Brush Zoom II D3 Component. -

-
-
-
-
-
- + + +
Brush & Zoom II
+
+ +
+
+

+ Demonstrate how to: +

+
    +
  • + build an Angular component which uses a D3 service to render to + <svg>, and +
  • +
  • + use D3 version 4 in TypeScript to draw a scatter plot with brush and zoom/pan event handling. +
  • +
+
+
+
+
+

Reference

+

+ This example is based on Mike Bostock's Brush & Zoom II Block. The original source with its supporting explanation + can be found by following the link below. +

+ Source: + + Brush & Zoom II Block + +
+
+

D3 Angular Implementation

+

+ The implementation is a straightforward Angular component without any inputs. Its template is an <svg> element with fixed dimensions. The component renders the visualization using the d3 instance provided + by the injected D3Service. +

+

+ The D3 component can be embedded into a parent component using the following tag: +

+

+ <app-brush-zoom-2></app-brush-zoom-2>. +

+ The source code for the D3 component can be found at: + + Brush Zoom II D3 Component + +
+
-
-
+
+ +
+ + diff --git a/src/app/d3-demos/wrapper-brush-zoom-2/wrapper-brush-zoom-2.component.spec.ts b/src/app/d3-demos/wrapper-brush-zoom-2/wrapper-brush-zoom-2.component.spec.ts index 390d8f3..694d346 100644 --- a/src/app/d3-demos/wrapper-brush-zoom-2/wrapper-brush-zoom-2.component.spec.ts +++ b/src/app/d3-demos/wrapper-brush-zoom-2/wrapper-brush-zoom-2.component.spec.ts @@ -4,6 +4,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { Component } from '@angular/core'; import { By } from '@angular/platform-browser'; +import { + MatCardModule, + MatListModule +} from '@angular/material'; + +import { + FlexLayoutModule +} from '@angular/flex-layout'; + import { WrapperBrushZoom2Component } from './wrapper-brush-zoom-2.component'; let fixture: ComponentFixture; @@ -23,6 +32,11 @@ describe('Component: WrapperBrushZoom2', () => { declarations: [ WrapperBrushZoom2Component, MockChildComponent + ], + imports: [ + FlexLayoutModule, + MatCardModule, + MatListModule ] }); @@ -40,22 +54,22 @@ describe('Component: WrapperBrushZoom2', () => { }); - it(`should have a div with class 'container' as its only child HTMLElement`, () => { + it(`should have a mat-card element as its only child DOM Element`, () => { let nativeEls: HTMLCollection | undefined[]; nativeEls = compiled ? compiled.children : []; expect(nativeEls.length).toBe(1, 'Incorrect number of elements found'); if (nativeEls.length === 1) { - expect(nativeEls[0].tagName === 'div' || nativeEls[0].tagName === 'DIV').toBeTruthy('Not a "div" element.'); - expect(nativeEls[0].classList.contains('container')).toBeTruthy('Not of class "container".'); + expect(nativeEls[0].tagName === 'mat-card' || nativeEls[0].tagName === 'MAT-CARD').toBeTruthy('Not a "mat-card" element.'); } }); - it(`should have heading h3 with text 'Brush & Zoom II'`, () => { - let nativeEls: NodeListOf | undefined[]; - nativeEls = compiled ? compiled.querySelectorAll('h3') : []; + it(`should have headline div with class 'mat-headline' and text content 'Brush & Zoom II'`, () => { + let nativeEls: NodeListOf | HTMLDivElement[]; + // HACK: Using 'as' due to TS issue with querySelectorAll signature in TS 2.5.3 + nativeEls = compiled ? compiled.querySelectorAll('div.mat-headline') as NodeListOf : []; expect(nativeEls.length).toBe(1, 'Incorrect number of elements found'); if (nativeEls.length === 1) { - let nativeEl = nativeEls[0]; + const nativeEl = nativeEls[0]; expect(nativeEl.textContent).toBe('Brush & Zoom II'); } }); diff --git a/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.css b/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.css index 697c563..4a7c829 100644 --- a/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.css +++ b/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.css @@ -1,19 +1,5 @@ -div.container { - background-color: rgb(245, 250, 245); - margin-top: 10px; - margin-bottom: 10px; - border-radius: 10px; -} - -div.col { - padding-top: 10px; - padding-bottom: 5px; -} - -h3 { - border-bottom: 1px solid #337ab7; - padding-top: 10px; - margin-bottom: 20px; +.mat-card { + margin: 8px; } div.visualization { @@ -26,3 +12,22 @@ div.visualization { margin: 10px auto 15px auto; overflow-x: auto; } + +.settings-table th { + font-weight: bold; + text-align: start; + padding: 5px; +} + +.settings-table th.text-center { + text-align: center; +} + +.settings-table td { + text-align: start; + padding: 5px; +} + +.text-justify { + text-align: justify; +} diff --git a/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.html b/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.html index b9873d3..ba70133 100644 --- a/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.html +++ b/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.html @@ -1,111 +1,104 @@ -
-
-
-

Drag & Zoom II

-
-
-
-
-

- Demonstrate how to: -

-
    -
  • - build an Angular component which uses a D3 service to render to <svg>, -
  • -
  • - use D3 version 4 in TypeScript to handle drag and zoom/pan events, and -
  • -
  • - use Angular to respond to input changes to a reusable D3 component. -
  • -
-
-
-
-
-

Reference

-

- This example is based on Mike Bostock's Drag & Zoom II Block. The original source with its supporting explanation - can be found by following the link below. -

-

- Source: Drag & Zoom II Block. -

-
-
-

D3 Angular Implementation

-

- The implementation is an Angular component with inputs. -

-

- The component supports inputs controlling key layout determinants: height, width, the radius of the phyllotaxis layout, - and the point radius. Its template is a plain <svg> element. -

-

- The component renders the visualization using the d3 instance provided by the injected D3Service. - When input values change, the visualization is redrawn. -

-

- The D3 component can be embedded into a parent component using the following tag: -

-

- <app-drag-zoom-2 [width]="960" [height]="500" [phylloRadius]="10" [pointRadius]="2.5"></app-drag-zoom-2>. -

-

- The numeric example inputs above reflect the choices of the reference source. However, in true Angular manner the component - is reusable and can obtain input values from parent component properties, as shown below. -

-

- The source code for the D3 component and its parent component can be found at: -

-

- Drag Zoom II D3 Component. -

-

- Drag Zoom II Wrapper Parent Component. -

+ + +
Drag & Zoom II
+
+ +
+
+

+ Demonstrate how to: +

+
    +
  • + build an Angular component which uses a D3 service to render to + <svg>, +
  • +
  • + use D3 version 4 in TypeScript to handle drag and zoom/pan events, and +
  • +
  • + use Angular to respond to input changes to a reusable D3 component. +
  • +
+
+
+
+
+

Reference

+

+ This example is based on + Mike Bostock's Drag & Zoom II Block. The original source with its supporting explanation can be found by + following the link below. +

+ Source: + + Drag & Zoom II Block + +
+
+

D3 Angular Implementation

+

+ The implementation is a straightforward Angular component without any inputs. Its template is an + <svg> element with fixed dimensions. The component renders the visualization using the + d3 instance provided by the injected + D3Service. +

+

+ The D3 component can be embedded into a parent component using the following tag: +

+

+ + <app-brush-zoom-2 [width]="width" [height]="height" [phylloRadius]="phylloRadius" [pointRadius]="pointRadius"></app-brush-zoom-2> + . +

+ The source code for the D3 component can be found at: + + Brush Zoom II D3 Component + +
+
+
-
-
-
-
- +
+
+
- + - + - + - +
- Choose Layout: - - + + Choose Layout: + + + {{layout.name}} + +
WidthWidth {{selectedLayout.width}}HeightHeight {{selectedLayout.height}}
Radius (Phyllotaxis)Radius (Phyllotaxis) {{selectedLayout.phylloRadius}}Radius (Point)Radius (Point) {{selectedLayout.pointRadius}}
-
-
-
- +
+
+ +
-
-
+ + diff --git a/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.spec.ts b/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.spec.ts index ba5e484..806f3d4 100644 --- a/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.spec.ts +++ b/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.spec.ts @@ -8,6 +8,15 @@ import { DragZoom2Component } from '../drag-zoom-2/drag-zoom-2.component'; import { D3Service } from 'd3-ng2-service'; +import { + MatButtonToggleModule, + MatCardModule, + MatListModule +} from '@angular/material'; + +import { + FlexLayoutModule +} from '@angular/flex-layout'; let fixture: ComponentFixture; let component: WrapperDragZoom2Component; @@ -23,6 +32,12 @@ describe('Component: WrapperDragZoom2', () => { ], providers: [ D3Service + ], + imports: [ + FlexLayoutModule, + MatButtonToggleModule, + MatCardModule, + MatListModule ] }); @@ -39,22 +54,22 @@ describe('Component: WrapperDragZoom2', () => { expect(component).toBeTruthy(); }); - it(`should have a div with class 'container' as its only child HTMLElement`, () => { + it(`should have a mat-card element as its only child DOM Element`, () => { let nativeEls: HTMLCollection | undefined[]; nativeEls = compiled ? compiled.children : []; expect(nativeEls.length).toBe(1, 'Incorrect number of elements found'); if (nativeEls.length === 1) { - expect(nativeEls[0].tagName === 'div' || nativeEls[0].tagName === 'DIV').toBeTruthy('Not a "div" element.'); - expect(nativeEls[0].classList.contains('container')).toBeTruthy('Not of class "container".'); + expect(nativeEls[0].tagName === 'mat-card' || nativeEls[0].tagName === 'MAT-CARD').toBeTruthy('Not a "mat-card" element.'); } }); - it(`should have heading h3 with text 'Drag & Zoom II'`, () => { - let nativeEls: NodeListOf | undefined[]; - nativeEls = compiled ? compiled.querySelectorAll('h3') : []; + it(`should have headline div with class 'mat-headline' and text content 'Drag & Zoom II'`, () => { + let nativeEls: NodeListOf | HTMLDivElement[]; + // HACK: Using 'as' due to TS issue with querySelectorAll signature in TS 2.5.3 + nativeEls = compiled ? compiled.querySelectorAll('div.mat-headline') as NodeListOf : []; expect(nativeEls.length).toBe(1, 'Incorrect number of elements found'); if (nativeEls.length === 1) { - let nativeEl = nativeEls[0]; + const nativeEl = nativeEls[0]; expect(nativeEl.textContent).toBe('Drag & Zoom II'); } }); diff --git a/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.ts b/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.ts index 83bd784..c98570d 100644 --- a/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.ts +++ b/src/app/d3-demos/wrapper-drag-zoom-2/wrapper-drag-zoom-2.component.ts @@ -1,6 +1,5 @@ import { Component, Input, OnInit } from '@angular/core'; - export interface DragZoom2Layout { name: string; label: string; @@ -38,6 +37,7 @@ export class WrapperDragZoom2Component implements OnInit { pointRadius: 4 } ]; + public selectedLayoutName = 'small'; ngOnInit() { if (this.selectedLayout === undefined) { @@ -49,5 +49,4 @@ export class WrapperDragZoom2Component implements OnInit { this.selectedLayout = layout; } - } diff --git a/src/app/d3-demos/wrapper-voronoi-spirals-3/wrapper-voronoi-spirals-3.component.css b/src/app/d3-demos/wrapper-voronoi-spirals-3/wrapper-voronoi-spirals-3.component.css index 697c563..3f14dc6 100644 --- a/src/app/d3-demos/wrapper-voronoi-spirals-3/wrapper-voronoi-spirals-3.component.css +++ b/src/app/d3-demos/wrapper-voronoi-spirals-3/wrapper-voronoi-spirals-3.component.css @@ -1,19 +1,5 @@ -div.container { - background-color: rgb(245, 250, 245); - margin-top: 10px; - margin-bottom: 10px; - border-radius: 10px; -} - -div.col { - padding-top: 10px; - padding-bottom: 5px; -} - -h3 { - border-bottom: 1px solid #337ab7; - padding-top: 10px; - margin-bottom: 20px; +.mat-card { + margin: 8px; } div.visualization { @@ -26,3 +12,7 @@ div.visualization { margin: 10px auto 15px auto; overflow-x: auto; } + +.text-justify { + text-align: justify; +} diff --git a/src/app/d3-demos/wrapper-voronoi-spirals-3/wrapper-voronoi-spirals-3.component.html b/src/app/d3-demos/wrapper-voronoi-spirals-3/wrapper-voronoi-spirals-3.component.html index 4ea5d0c..45608af 100644 --- a/src/app/d3-demos/wrapper-voronoi-spirals-3/wrapper-voronoi-spirals-3.component.html +++ b/src/app/d3-demos/wrapper-voronoi-spirals-3/wrapper-voronoi-spirals-3.component.html @@ -1,60 +1,61 @@ -
-
-
-

Voronoi Spirals III

-
-
-
-
-

- Demonstrate how to: -

-
    -
  • - build an Angular component which uses a D3 service to render to <canvas>, -
  • -
  • - use D3 version 4 in TypeScript for Voronoi polygons. -
  • -
-
-
-
-
-

Reference

-

- This example is based on Mike Bostock's Voronoi Spirals III Block. The original source with its supporting - explanation can be found by following the link below. -

-

- Source: Voronoi Spirals III Block. -

-
-
-

D3 Angular Implementation

-

- The implementation is a straightforward Angular component without any inputs. Its template is a <canvas> element with fixed dimensions. The component renders the visualization using the d3 instance provided - by the injected D3Service. -

-

- The D3 component can be embedded into a parent component using the following tag: -

-

- <app-voronoi-spirals-3></app-voronoi-spirals-3>. -

-

- The source code for the D3 component can be found at: -

-

- Voronoi Spirals III D3 Component. -

-
-
-
-
-
- + + +
Voronoi Spirals III
+
+ +
+
+

+ Demonstrate how to: +

+
    +
  • + build an Angular component which uses a D3 service to render to + <canvas>, +
  • +
  • + use D3 version 4 in TypeScript for Voronoi polygons. +
  • +
+
+
+
+
+

Reference

+

+ This example is based on + Mike Bostock's Voronoi Spirals III Block. The original source with its supporting explanation can be found + by following the link below. +

+ Source: + + Voronoi Spirals III Block + +
+
+

D3 Angular Implementation

+

+ The implementation is a straightforward Angular component without any inputs. Its template is a + <canvas> element with fixed dimensions. The component renders the visualization using the + d3 instance provided by the injected + D3Service. +

+

+ The D3 component can be embedded into a parent component using the following tag: +

+

+ <app-voronoi-spirals-3></app-voronoi-spirals-3>. +

+ The source code for the D3 component can be found at: + + Voronoi Spirals III D3 Component + +
+
-
-
+
+ +
+ + diff --git a/src/app/d3-demos/wrapper-voronoi-spirals-3/wrapper-voronoi-spirals-3.component.spec.ts b/src/app/d3-demos/wrapper-voronoi-spirals-3/wrapper-voronoi-spirals-3.component.spec.ts index 1669739..314c92d 100644 --- a/src/app/d3-demos/wrapper-voronoi-spirals-3/wrapper-voronoi-spirals-3.component.spec.ts +++ b/src/app/d3-demos/wrapper-voronoi-spirals-3/wrapper-voronoi-spirals-3.component.spec.ts @@ -4,6 +4,16 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { Component } from '@angular/core'; import { By } from '@angular/platform-browser'; +import { + MatCardModule, + MatListModule +} from '@angular/material'; + +import { + FlexLayoutModule +} from '@angular/flex-layout'; + + import { WrapperVoronoiSpirals3Component } from './wrapper-voronoi-spirals-3.component'; let fixture: ComponentFixture; @@ -14,7 +24,7 @@ let compiled: HTMLElement | null; selector: 'app-voronoi-spirals-3', template: `` }) -class MockChildComponent {}; +class MockChildComponent {} describe('Component: WrapperVoronoiSpirals3', () => { @@ -24,6 +34,11 @@ describe('Component: WrapperVoronoiSpirals3', () => { declarations: [ WrapperVoronoiSpirals3Component, MockChildComponent + ], + imports: [ + FlexLayoutModule, + MatCardModule, + MatListModule ] }); @@ -40,22 +55,22 @@ describe('Component: WrapperVoronoiSpirals3', () => { expect(component).toBeTruthy(); }); - it(`should have a div with class 'container' as its only child HTMLElement`, () => { + it(`should have a mat-card element as its only child DOM Element`, () => { let nativeEls: HTMLCollection | undefined[]; nativeEls = compiled ? compiled.children : []; expect(nativeEls.length).toBe(1, 'Incorrect number of elements found'); if (nativeEls.length === 1) { - expect(nativeEls[0].tagName === 'div' || nativeEls[0].tagName === 'DIV').toBeTruthy('Not a "div" element.'); - expect(nativeEls[0].classList.contains('container')).toBeTruthy('Not of class "container".'); + expect(nativeEls[0].tagName === 'mat-card' || nativeEls[0].tagName === 'MAT-CARD').toBeTruthy('Not a "mat-card" element.'); } }); - it(`should have heading h3 with text 'Voronoi Spirals III'`, () => { - let nativeEls: NodeListOf | undefined[]; - nativeEls = compiled ? compiled.querySelectorAll('h3') : []; + it(`should have headline div with class 'mat-headline' and text content 'Voronoi Spirals III'`, () => { + let nativeEls: NodeListOf | HTMLDivElement[]; + // HACK: Using 'as' due to TS issue with querySelectorAll signature in TS 2.5.3 + nativeEls = compiled ? compiled.querySelectorAll('div.mat-headline') as NodeListOf : []; expect(nativeEls.length).toBe(1, 'Incorrect number of elements found'); if (nativeEls.length === 1) { - let nativeEl = nativeEls[0]; + const nativeEl = nativeEls[0]; expect(nativeEl.textContent).toBe('Voronoi Spirals III'); } }); diff --git a/src/assets/GitHub-Mark-32px.png b/src/assets/GitHub-Mark-32px.png new file mode 100644 index 0000000..8b25551 Binary files /dev/null and b/src/assets/GitHub-Mark-32px.png differ diff --git a/src/assets/GitHub-Mark-Light-32px.png b/src/assets/GitHub-Mark-Light-32px.png new file mode 100644 index 0000000..628da97 Binary files /dev/null and b/src/assets/GitHub-Mark-Light-32px.png differ diff --git a/src/index.html b/src/index.html index 19bbf75..918e3bf 100644 --- a/src/index.html +++ b/src/index.html @@ -12,15 +12,7 @@ -
-
-
-
- Loading... -
-
-
-
+ Loading...
diff --git a/src/my-theme.scss b/src/my-theme.scss new file mode 100644 index 0000000..9aa5bd7 --- /dev/null +++ b/src/my-theme.scss @@ -0,0 +1,14 @@ +@import '~@angular/material/theming'; +@import './_extra-colors.scss'; + +@include mat-core(); + +$sharp-light-primary: mat-palette($mat-indigo, 400, 200, 800); +$sharp-light-accent: mat-palette($mat-pink, A200, 100, A700); + +$sharp-light-warn: mat-palette($mat-red); + +$sharp-light-theme: mat-light-theme($sharp-light-primary, $sharp-light-accent, $sharp-light-warn); + +@include angular-material-theme($sharp-light-theme); +@include extra-color-classes($sharp-light-theme); diff --git a/src/styles.css b/src/styles.css deleted file mode 100644 index 63c15f5..0000000 --- a/src/styles.css +++ /dev/null @@ -1,6 +0,0 @@ -/* You can add global styles to this file, and also import other style files */ - -body { - background-color: slateblue; - padding-top: 70px; -} diff --git a/src/styles.scss b/src/styles.scss new file mode 100644 index 0000000..4dd4a0d --- /dev/null +++ b/src/styles.scss @@ -0,0 +1,11 @@ +html, body { + display: flex; + flex-direction: column; + font-family: Roboto, Arial, sans-serif; + margin: 0; + height: 100%; +} + +.uppercase { + text-transform: uppercase; +}