From bef0dc828143b71d732c7da6e643df2d548a60ee Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Thu, 16 Dec 2021 00:26:30 -0800 Subject: [PATCH 1/2] feat: adding color picker component --- package-lock.json | 48 ++++++++++ package.json | 1 + projects/components/package.json | 3 +- .../color-picker/color-picker.component.scss | 31 +++++++ .../color-picker.component.test.ts | 38 ++++++++ .../color-picker/color-picker.component.ts | 89 +++++++++++++++++++ .../src/color-picker/color-picker.module.ts | 14 +++ .../src/form-field/form-field.component.scss | 1 + projects/components/src/public-api.ts | 4 + 9 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 projects/components/src/color-picker/color-picker.component.scss create mode 100644 projects/components/src/color-picker/color-picker.component.test.ts create mode 100644 projects/components/src/color-picker/color-picker.component.ts create mode 100644 projects/components/src/color-picker/color-picker.module.ts diff --git a/package-lock.json b/package-lock.json index ce4c72a58..97523cc85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "iso8601-duration": "^1.3.0", "lodash-es": "^4.17.21", "mixpanel-browser": "^2.42.0", + "ngx-color": "^7.3.3", "rxjs": "~6.6.7", "tslib": "^2.3.1", "uuid": "^8.3.2", @@ -3789,6 +3790,14 @@ "node": ">=4.0.0" } }, + "node_modules/@ctrl/tinycolor": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz", + "integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz", @@ -20305,6 +20314,11 @@ "node": ">= 12" } }, + "node_modules/material-colors": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", + "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==" + }, "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -21056,6 +21070,20 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/ngx-color": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/ngx-color/-/ngx-color-7.3.3.tgz", + "integrity": "sha512-RyMIFMC5/aYYD/jkfStOUjr3gQfTGhgiiMxuZEfxt2o4GYmb3C/06C1o0S6Mj9qHAcKlG6soioq2MzdhtIswHw==", + "dependencies": { + "@ctrl/tinycolor": "^3.4.0", + "material-colors": "^1.2.6", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=12.0.0-0", + "@angular/core": ">=12.0.0-0" + } + }, "node_modules/nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", @@ -33006,6 +33034,11 @@ "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==", "dev": true }, + "@ctrl/tinycolor": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz", + "integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ==" + }, "@discoveryjs/json-ext": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz", @@ -46187,6 +46220,11 @@ "integrity": "sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==", "dev": true }, + "material-colors": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", + "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==" + }, "mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -46785,6 +46823,16 @@ } } }, + "ngx-color": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/ngx-color/-/ngx-color-7.3.3.tgz", + "integrity": "sha512-RyMIFMC5/aYYD/jkfStOUjr3gQfTGhgiiMxuZEfxt2o4GYmb3C/06C1o0S6Mj9qHAcKlG6soioq2MzdhtIswHw==", + "requires": { + "@ctrl/tinycolor": "^3.4.0", + "material-colors": "^1.2.6", + "tslib": "^2.3.0" + } + }, "nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", diff --git a/package.json b/package.json index f105ee1eb..b47317ef2 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "iso8601-duration": "^1.3.0", "lodash-es": "^4.17.21", "mixpanel-browser": "^2.42.0", + "ngx-color": "^7.3.3", "rxjs": "~6.6.7", "tslib": "^2.3.1", "uuid": "^8.3.2", diff --git a/projects/components/package.json b/projects/components/package.json index 7d065a6cb..7c52b0f79 100644 --- a/projects/components/package.json +++ b/projects/components/package.json @@ -27,7 +27,8 @@ "d3-axis": "^2.1.0", "d3-scale": "^3.3.0", "d3-selection": "^1.4.2", - "d3-shape": "^1.3.5" + "d3-shape": "^1.3.5", + "ngx-color": "^7.3.3" }, "devDependencies": { "@hypertrace/test-utils": "^0.0.0" diff --git a/projects/components/src/color-picker/color-picker.component.scss b/projects/components/src/color-picker/color-picker.component.scss new file mode 100644 index 000000000..27f4a2327 --- /dev/null +++ b/projects/components/src/color-picker/color-picker.component.scss @@ -0,0 +1,31 @@ +@import 'color-palette'; +@import 'mixins'; + +.color-picker { + display: flex; + flex-flow: row wrap; + align-items: center; + + .color { + width: 24px; + height: 24px; + border-radius: 50%; + margin-right: 6px; + cursor: pointer; + + &.selected { + border: 2px solid $blue-4; + } + } +} + +.container { + width: 200px; + height: 200px; + cursor: pointer; +} + +.color-sketch { + width: 200px; + height: 200px; +} diff --git a/projects/components/src/color-picker/color-picker.component.test.ts b/projects/components/src/color-picker/color-picker.component.test.ts new file mode 100644 index 000000000..9e31ac9bb --- /dev/null +++ b/projects/components/src/color-picker/color-picker.component.test.ts @@ -0,0 +1,38 @@ +import { IconType } from '@hypertrace/assets-library'; +import { IconComponent } from '@hypertrace/components'; +import { createHostFactory, mockProvider, Spectator } from '@ngneat/spectator/jest'; +import { ColorPickerComponent } from './color-picker.component'; +import { SketchComponent } from 'ngx-color/sketch'; +import { PopoverModule } from '../popover/popover.module'; +import { NotificationService } from '../notification/notification.service'; +import { CommonModule } from '@angular/common'; +import { Observable } from 'rxjs'; +import { MockComponent } from 'ng-mocks'; + +describe('Color Picker component', () => { + let spectator: Spectator; + + const createHost = createHostFactory({ + component: ColorPickerComponent, + imports: [CommonModule, PopoverModule], + providers: [mockProvider(NotificationService, { withNotification: (x: Observable) => x })], + declareComponent: false, + declarations: [MockComponent(SketchComponent), MockComponent(IconComponent)] + }); + + test('should render color picker with default colors', () => { + const onSelectionChangeSpy = jest.fn(); + spectator = createHost( + ` + `, + { + hostProps: { + onSelectionChange: onSelectionChangeSpy + } + } + ); + + expect(spectator.queryAll('.color-picker .color').length).toBe(7); + expect(spectator.query(IconComponent)?.icon).toBe(IconType.Add); + }); +}); diff --git a/projects/components/src/color-picker/color-picker.component.ts b/projects/components/src/color-picker/color-picker.component.ts new file mode 100644 index 000000000..d4dc01149 --- /dev/null +++ b/projects/components/src/color-picker/color-picker.component.ts @@ -0,0 +1,89 @@ +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { NG_VALUE_ACCESSOR } from '@angular/forms'; +import { IconType } from '@hypertrace/assets-library'; +import { Color } from '@hypertrace/common'; +import { IconSize } from '../icon/icon-size'; + +@Component({ + selector: 'ht-color-picker', + styleUrls: ['./color-picker.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + multi: true, + useExisting: ColorPickerComponent + } + ], + template: ` +
+
+ + + + + +
+ +
+
+
+
+ ` +}) +export class ColorPickerComponent { + @Input() + public selected?: string; + + @Output() + private readonly selectedChange: EventEmitter = new EventEmitter(); + + private readonly paletteSet: Set = new Set([ + Color.Brown1, + Color.Blue3, + Color.Green3, + Color.Orange3, + Color.Purple3, + Color.Red3, + Color.Yellow3 + ]); + public paletteColors: string[] = Array.from(this.paletteSet); + + private propagateControlValueChange?: (value: string | undefined) => void; + private propagateControlValueChangeOnTouch?: (value: string | undefined) => void; + + public onAddColorToPalette(color: string): void { + this.paletteSet.add(color); + this.paletteColors = Array.from(this.paletteSet); + this.selectColor(color); + } + + public selectColor(color: string): void { + this.selected = color; + this.selectedChange.emit(color); + this.propagateValueChangeToFormControl(color); + } + + private propagateValueChangeToFormControl(value?: string): void { + this.propagateControlValueChange?.(value); + this.propagateControlValueChangeOnTouch?.(value); + } + + public writeValue(color?: string): void { + this.selected = color; + } + + public registerOnChange(onChange: (value?: string) => void): void { + this.propagateControlValueChange = onChange; + } + + public registerOnTouched(onTouch: (value?: string) => void): void { + this.propagateControlValueChangeOnTouch = onTouch; + } +} diff --git a/projects/components/src/color-picker/color-picker.module.ts b/projects/components/src/color-picker/color-picker.module.ts new file mode 100644 index 000000000..d921cfb4d --- /dev/null +++ b/projects/components/src/color-picker/color-picker.module.ts @@ -0,0 +1,14 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ColorSketchModule } from 'ngx-color/sketch'; +import { IconModule } from '../icon/icon.module'; +import { PopoverModule } from './../popover/popover.module'; +import { ColorPickerComponent } from './color-picker.component'; + +@NgModule({ + imports: [CommonModule, FormsModule, IconModule, ColorSketchModule, PopoverModule], + declarations: [ColorPickerComponent], + exports: [ColorPickerComponent] +}) +export class ColorPickerModule {} diff --git a/projects/components/src/form-field/form-field.component.scss b/projects/components/src/form-field/form-field.component.scss index e6a0afe3b..32b1c663b 100644 --- a/projects/components/src/form-field/form-field.component.scss +++ b/projects/components/src/form-field/form-field.component.scss @@ -31,6 +31,7 @@ .content { flex: 1 1 auto; + display: flex; width: 100%; background: white; diff --git a/projects/components/src/public-api.ts b/projects/components/src/public-api.ts index 374277bae..3d030055d 100644 --- a/projects/components/src/public-api.ts +++ b/projects/components/src/public-api.ts @@ -37,6 +37,10 @@ export * from './tabs/content/tab-group.component'; export * from './tabs/content/tab.module'; export * from './tabs/content/tab/tab.component'; +// Color picker +export * from './color-picker/color-picker.component'; +export * from './color-picker/color-picker.module'; + // Copy to Clipboard export * from './copy-to-clipboard/copy-to-clipboard.component'; export * from './copy-to-clipboard/copy-to-clipboard.module'; From b4378e8200e05aa6e4245c1f78c8d2bf2967f1a5 Mon Sep 17 00:00:00 2001 From: anandtiwary <52081890+anandtiwary@users.noreply.github.com> Date: Thu, 16 Dec 2021 14:26:44 -0800 Subject: [PATCH 2/2] refactor: downgrading version and fixing tests --- package-lock.json | 18 +++++----- package.json | 2 +- projects/components/package.json | 2 +- .../color-picker.component.test.ts | 36 +++++++++++++------ .../color-picker/color-picker.component.ts | 2 +- tsconfig.base.json | 1 + 6 files changed, 39 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 97523cc85..e7f2b72c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,7 @@ "iso8601-duration": "^1.3.0", "lodash-es": "^4.17.21", "mixpanel-browser": "^2.42.0", - "ngx-color": "^7.3.3", + "ngx-color": "7.0.0", "rxjs": "~6.6.7", "tslib": "^2.3.1", "uuid": "^8.3.2", @@ -21071,13 +21071,13 @@ "dev": true }, "node_modules/ngx-color": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/ngx-color/-/ngx-color-7.3.3.tgz", - "integrity": "sha512-RyMIFMC5/aYYD/jkfStOUjr3gQfTGhgiiMxuZEfxt2o4GYmb3C/06C1o0S6Mj9qHAcKlG6soioq2MzdhtIswHw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ngx-color/-/ngx-color-7.0.0.tgz", + "integrity": "sha512-BiTapBTT/f3sFSEFqet3xe06bpqmBm7hmA24s09ogRYYVGL1J69U13XfLwTQG8PhX4qNBceh7p5nhKA1zczHMg==", "dependencies": { "@ctrl/tinycolor": "^3.4.0", "material-colors": "^1.2.6", - "tslib": "^2.3.0" + "tslib": "^2.1.0" }, "peerDependencies": { "@angular/common": ">=12.0.0-0", @@ -46824,13 +46824,13 @@ } }, "ngx-color": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/ngx-color/-/ngx-color-7.3.3.tgz", - "integrity": "sha512-RyMIFMC5/aYYD/jkfStOUjr3gQfTGhgiiMxuZEfxt2o4GYmb3C/06C1o0S6Mj9qHAcKlG6soioq2MzdhtIswHw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ngx-color/-/ngx-color-7.0.0.tgz", + "integrity": "sha512-BiTapBTT/f3sFSEFqet3xe06bpqmBm7hmA24s09ogRYYVGL1J69U13XfLwTQG8PhX4qNBceh7p5nhKA1zczHMg==", "requires": { "@ctrl/tinycolor": "^3.4.0", "material-colors": "^1.2.6", - "tslib": "^2.3.0" + "tslib": "^2.1.0" } }, "nice-napi": { diff --git a/package.json b/package.json index b47317ef2..9516ac9e4 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "iso8601-duration": "^1.3.0", "lodash-es": "^4.17.21", "mixpanel-browser": "^2.42.0", - "ngx-color": "^7.3.3", + "ngx-color": "7.0.0", "rxjs": "~6.6.7", "tslib": "^2.3.1", "uuid": "^8.3.2", diff --git a/projects/components/package.json b/projects/components/package.json index 7c52b0f79..16f9c2eec 100644 --- a/projects/components/package.json +++ b/projects/components/package.json @@ -28,7 +28,7 @@ "d3-scale": "^3.3.0", "d3-selection": "^1.4.2", "d3-shape": "^1.3.5", - "ngx-color": "^7.3.3" + "ngx-color": "7.0.0" }, "devDependencies": { "@hypertrace/test-utils": "^0.0.0" diff --git a/projects/components/src/color-picker/color-picker.component.test.ts b/projects/components/src/color-picker/color-picker.component.test.ts index 9e31ac9bb..0481c3111 100644 --- a/projects/components/src/color-picker/color-picker.component.test.ts +++ b/projects/components/src/color-picker/color-picker.component.test.ts @@ -1,13 +1,15 @@ +import { CommonModule } from '@angular/common'; +import { fakeAsync } from '@angular/core/testing'; import { IconType } from '@hypertrace/assets-library'; +import { Color, NavigationService } from '@hypertrace/common'; import { IconComponent } from '@hypertrace/components'; import { createHostFactory, mockProvider, Spectator } from '@ngneat/spectator/jest'; -import { ColorPickerComponent } from './color-picker.component'; +import { MockComponent } from 'ng-mocks'; import { SketchComponent } from 'ngx-color/sketch'; -import { PopoverModule } from '../popover/popover.module'; +import { NEVER, Observable } from 'rxjs'; import { NotificationService } from '../notification/notification.service'; -import { CommonModule } from '@angular/common'; -import { Observable } from 'rxjs'; -import { MockComponent } from 'ng-mocks'; +import { PopoverModule } from '../popover/popover.module'; +import { ColorPickerComponent } from './color-picker.component'; describe('Color Picker component', () => { let spectator: Spectator; @@ -15,12 +17,16 @@ describe('Color Picker component', () => { const createHost = createHostFactory({ component: ColorPickerComponent, imports: [CommonModule, PopoverModule], - providers: [mockProvider(NotificationService, { withNotification: (x: Observable) => x })], - declareComponent: false, + providers: [ + mockProvider(NotificationService, { withNotification: (x: Observable) => x }), + mockProvider(NavigationService, { + navigation$: NEVER + }) + ], declarations: [MockComponent(SketchComponent), MockComponent(IconComponent)] }); - test('should render color picker with default colors', () => { + test('should render color picker with default colors', fakeAsync(() => { const onSelectionChangeSpy = jest.fn(); spectator = createHost( ` @@ -32,7 +38,17 @@ describe('Color Picker component', () => { } ); - expect(spectator.queryAll('.color-picker .color').length).toBe(7); + const colors = spectator.queryAll('.color-picker .color'); + expect(colors.length).toBe(7); expect(spectator.query(IconComponent)?.icon).toBe(IconType.Add); - }); + + spectator.click(colors[1]); + expect(spectator.component.selected).toEqual(Color.Blue3); + expect(onSelectionChangeSpy).toHaveBeenCalledWith(Color.Blue3); + + spectator.click('.add-icon'); + spectator.tick(); + + expect(spectator.query('.color-sketch', { root: true })).toExist(); + })); }); diff --git a/projects/components/src/color-picker/color-picker.component.ts b/projects/components/src/color-picker/color-picker.component.ts index d4dc01149..7c3c1244e 100644 --- a/projects/components/src/color-picker/color-picker.component.ts +++ b/projects/components/src/color-picker/color-picker.component.ts @@ -26,7 +26,7 @@ import { IconSize } from '../icon/icon-size'; > - +
diff --git a/tsconfig.base.json b/tsconfig.base.json index 374e43474..141bd04e7 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -16,6 +16,7 @@ "noUnusedLocals": true, "noUnusedParameters": true, "downlevelIteration": true, + "allowJs": true, "lib": ["es2015", "es2016", "es2017", "esnext.string", "esnext.array", "esnext.asynciterable", "dom"], "paths": { "@hypertrace/assets-library": ["projects/assets-library/src/public-api.ts"],