From e4be400e740bc1e0cdcfabf217e47223059dbf06 Mon Sep 17 00:00:00 2001 From: Florent gravin Date: Mon, 14 Feb 2022 15:15:56 +0100 Subject: [PATCH 1/5] feat(badge): add clickeable option to BadgeComponent --- libs/ui/widgets/src/lib/badge/badge.component.html | 14 +++----------- libs/ui/widgets/src/lib/badge/badge.component.ts | 6 ++++-- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/libs/ui/widgets/src/lib/badge/badge.component.html b/libs/ui/widgets/src/lib/badge/badge.component.html index 31a85c0d0c..282b1eff0a 100644 --- a/libs/ui/widgets/src/lib/badge/badge.component.html +++ b/libs/ui/widgets/src/lib/badge/badge.component.html @@ -1,15 +1,7 @@
diff --git a/libs/ui/widgets/src/lib/badge/badge.component.ts b/libs/ui/widgets/src/lib/badge/badge.component.ts index 1fcdf69a7c..88f4053e36 100644 --- a/libs/ui/widgets/src/lib/badge/badge.component.ts +++ b/libs/ui/widgets/src/lib/badge/badge.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core' +import { ChangeDetectionStrategy, Component, Input } from '@angular/core' @Component({ selector: 'gn-ui-badge', @@ -6,4 +6,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core' styleUrls: ['./badge.component.css'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class BadgeComponent {} +export class BadgeComponent { + @Input() clickable = false +} From 46d73a7ec3cb2ecb117f161224c01e3a10014eff Mon Sep 17 00:00:00 2001 From: Florent gravin Date: Mon, 14 Feb 2022 15:16:30 +0100 Subject: [PATCH 2/5] feat(record): keyword click trigger a search it route to /search?q=keyword route --- .../lib/metadata-info/metadata-info.component.html | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html index 26bf14c67e..a3e7a7f80f 100644 --- a/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +++ b/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html @@ -20,6 +20,9 @@
{{ keyword }} @@ -34,15 +37,7 @@ {{ metadata.lineage }}

record.metadata.updatedOn

From 62acd3c075fde9771a03f885f00e96dea3612ddc Mon Sep 17 00:00:00 2001 From: Florent gravin Date: Mon, 14 Feb 2022 21:40:42 +0100 Subject: [PATCH 3/5] refactor(datahub): search route update set fuzzy-search value remote the take(1) to listen to all changes and not just on init rename initialValue to value --- .../search-header.component.html | 9 ++------ .../search-header.component.spec.ts | 21 +++++++++++++------ .../search-header/search-header.component.ts | 3 +-- .../fuzzy-search/fuzzy-search.component.html | 2 +- .../fuzzy-search/fuzzy-search.component.ts | 2 +- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/apps/datahub/src/app/search/search-header/search-header.component.html b/apps/datahub/src/app/search/search-header/search-header.component.html index a90f232a00..367e1659cc 100644 --- a/apps/datahub/src/app/search/search-header/search-header.component.html +++ b/apps/datahub/src/app/search/search-header/search-header.component.html @@ -8,7 +8,7 @@
diff --git a/apps/datahub/src/app/search/search-header/search-header.component.spec.ts b/apps/datahub/src/app/search/search-header/search-header.component.spec.ts index d2dabf2762..989cd827c6 100644 --- a/apps/datahub/src/app/search/search-header/search-header.component.spec.ts +++ b/apps/datahub/src/app/search/search-header/search-header.component.spec.ts @@ -9,9 +9,9 @@ import { BehaviorSubject } from 'rxjs' import { SearchHeaderComponent } from './search-header.component' -class RouterFacadeMock { - goToMetadata = jest.fn() - anySearch$ = new BehaviorSubject('scot') +const routerFacadeMock = { + goToMetadata: jest.fn(), + anySearch$: new BehaviorSubject('scot'), } class SearchFacadeMock {} /* eslint-disable */ @@ -20,7 +20,7 @@ class SearchFacadeMock {} template: '', }) class FuzzySearchComponentMock { - @Input() initialValue?: MetadataRecord + @Input() value?: MetadataRecord } /* eslint-enable */ @@ -36,7 +36,7 @@ describe('HeaderComponent', () => { providers: [ { provide: RouterFacade, - useClass: RouterFacadeMock, + useValue: routerFacadeMock, }, { provide: SearchFacade, @@ -61,7 +61,16 @@ describe('HeaderComponent', () => { const fuzzyCpt = fixture.debugElement.query( By.directive(FuzzySearchComponentMock) ).componentInstance - expect(fuzzyCpt.initialValue).toEqual({ title: 'scot' }) + expect(fuzzyCpt.value).toEqual({ title: 'scot' }) + }) + it('value is changed on route update', () => { + routerFacadeMock.anySearch$.next('river') + const fuzzyCpt = fixture.debugElement.query( + By.directive(FuzzySearchComponentMock) + ).componentInstance + fixture.detectChanges() + + expect(fuzzyCpt.value).toEqual({ title: 'river' }) }) }) }) diff --git a/apps/datahub/src/app/search/search-header/search-header.component.ts b/apps/datahub/src/app/search/search-header/search-header.component.ts index 7b286c35d7..866841a57b 100644 --- a/apps/datahub/src/app/search/search-header/search-header.component.ts +++ b/apps/datahub/src/app/search/search-header/search-header.component.ts @@ -3,7 +3,7 @@ import { marker } from '@biesbjerg/ngx-translate-extract-marker' import { RouterFacade } from '@geonetwork-ui/feature/router' import { SearchFacade } from '@geonetwork-ui/feature/search' import { MetadataRecord } from '@geonetwork-ui/util/shared' -import { map, take } from 'rxjs/operators' +import { map } from 'rxjs/operators' marker('datahub.header.myfavorites') marker('datahub.header.connex') @@ -18,7 +18,6 @@ marker('datahub.header.popularRecords') }) export class SearchHeaderComponent { searchInputRouteValue$ = this.routerFacade.anySearch$.pipe( - take(1), map((any) => ({ title: any })) ) diff --git a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.html b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.html index 9cedcdc5c5..479a41c2d5 100644 --- a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.html +++ b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.html @@ -4,6 +4,6 @@ [action]="autoCompleteAction" (itemSelected)="handleItemSelection($event)" (inputSubmited)="handleInputSubmission($event)" - [initialValue]="initialValue" + [value]="value" [clearOnSelection]="true" > diff --git a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts index c18c0d9bfb..85bfb31f00 100644 --- a/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts +++ b/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts @@ -27,7 +27,7 @@ import { ElasticsearchMapper } from '../utils/mapper' changeDetection: ChangeDetectionStrategy.OnPush, }) export class FuzzySearchComponent { - @Input() initialValue?: MetadataRecord + @Input() value?: MetadataRecord @ViewChild(AutocompleteComponent) autocomplete: AutocompleteComponent @Output() itemSelected = new EventEmitter() @Output() inputSubmited = new EventEmitter() From 952a0a36eb2abc5fce91cfef930b385ad61d59e5 Mon Sep 17 00:00:00 2001 From: Florent gravin Date: Mon, 14 Feb 2022 22:02:00 +0100 Subject: [PATCH 4/5] refactor(autocomplete): update the input value from @input initialValue becomes value initInput changed to updateInput updateInput is called in ngChanges instead of ngOnInit --- .../autocomplete.component.spec.ts | 63 +++++++++++++++++-- .../autocomplete/autocomplete.component.ts | 25 ++++++-- 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.spec.ts b/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.spec.ts index bf413d036f..f6218ab5c5 100644 --- a/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.spec.ts +++ b/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.spec.ts @@ -117,14 +117,19 @@ describe('AutocompleteComponent', () => { }) }) - describe('initial value', () => { + describe('@Input() value', () => { let anyEmitted describe('when set', () => { beforeEach(() => { - component.initialValue = { title: 'hello' } - component.displayWithFn = (item) => item.title + const simpleChanges: any = { + value: { + previousValue: undefined, + currentValue: { title: 'hello' }, + }, + } + component.displayWithFn = (item) => item?.title component.inputSubmited.subscribe((event) => (anyEmitted = event)) - fixture.detectChanges() + component.ngOnChanges(simpleChanges) }) it('set control value', () => { expect(component.control.value).toEqual({ title: 'hello' }) @@ -133,10 +138,56 @@ describe('AutocompleteComponent', () => { expect(anyEmitted).toEqual('hello') }) }) - describe('when not set', () => { + describe('when changed', () => { beforeEach(() => { + const simpleChanges: any = { + value: { + previousValue: { title: 'hello' }, + currentValue: { title: 'good bye' }, + }, + } + component.displayWithFn = (item) => item?.title component.inputSubmited.subscribe((event) => (anyEmitted = event)) - fixture.detectChanges() + component.ngOnChanges(simpleChanges) + }) + it('set control value', () => { + expect(component.control.value).toEqual({ title: 'good bye' }) + }) + it('emits any string', () => { + expect(anyEmitted).toEqual('good bye') + }) + }) + describe('when ref changed but same text', () => { + let anyEmitted + beforeEach(() => { + const simpleChanges: any = { + value: { + previousValue: { title: 'good bye' }, + currentValue: { title: 'good bye' }, + }, + } + component.displayWithFn = (item) => item?.title + component.inputSubmited.subscribe((event) => (anyEmitted = event)) + component.ngOnChanges(simpleChanges) + }) + it('does not set control value', () => { + expect(component.control.value).toBeNull() + }) + it('does not emit any value', () => { + expect(anyEmitted).toBeUndefined() + }) + }) + describe('when not set on init (firstChange == true)', () => { + beforeEach(() => { + component.inputSubmited.subscribe((event) => (anyEmitted = event)) + const simpleChanges: any = { + value: { + firstChange: true, + previousValue: undefined, + currentValue: null, + }, + } + component.ngOnChanges(simpleChanges) }) it('does not set control value', () => { expect(component.control.value).toEqual(null) diff --git a/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts b/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts index 002ab24c17..2b4a87420d 100644 --- a/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +++ b/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts @@ -5,9 +5,11 @@ import { ElementRef, EventEmitter, Input, + OnChanges, OnDestroy, OnInit, Output, + SimpleChanges, ViewChild, } from '@angular/core' import { FormControl } from '@angular/forms' @@ -16,7 +18,7 @@ import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger, } from '@angular/material/autocomplete' -import { BehaviorSubject, Observable, ReplaySubject, Subscription } from 'rxjs' +import { Observable, ReplaySubject, Subscription } from 'rxjs' import { debounceTime, distinctUntilChanged, @@ -36,10 +38,12 @@ export type AutcompleteItem = unknown styleUrls: ['./autocomplete.component.css'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AutocompleteComponent implements OnInit, AfterViewInit, OnDestroy { +export class AutocompleteComponent + implements OnInit, AfterViewInit, OnDestroy, OnChanges +{ @Input() placeholder: string @Input() action: (value: string) => Observable - @Input() initialValue?: AutcompleteItem + @Input() value?: AutcompleteItem @Input() clearOnSelection = false @Output() itemSelected = new EventEmitter() @Output() inputSubmited = new EventEmitter() @@ -57,6 +61,17 @@ export class AutocompleteComponent implements OnInit, AfterViewInit, OnDestroy { @Input() displayWithFn: (AutcompleteItem) => string = (item) => item + ngOnChanges(changes: SimpleChanges): void { + const { value } = changes + if (value) { + const previousTextValue = this.displayWithFn(value.previousValue) + const currentTextValue = this.displayWithFn(value.currentValue) + if (value.firstChange || previousTextValue !== currentTextValue) { + this.updateInputValue(value.currentValue) + } + } + } + ngOnInit(): void { this.suggestions$ = this.control.valueChanges.pipe( filter((value) => value.length > 2), @@ -74,8 +89,6 @@ export class AutocompleteComponent implements OnInit, AfterViewInit, OnDestroy { this.control.valueChanges .pipe(filter((value) => typeof value === 'string')) .subscribe(this.lastInputValue$) - - this.initInput(this.initialValue) } ngAfterViewInit(): void { @@ -86,7 +99,7 @@ export class AutocompleteComponent implements OnInit, AfterViewInit, OnDestroy { this.subscription.unsubscribe() } - initInput(value: AutcompleteItem) { + updateInputValue(value: AutcompleteItem) { if (value) { this.control.setValue(value) } From b275f47eebd40bcde8f9e79a1983412283158134 Mon Sep 17 00:00:00 2001 From: Florent gravin Date: Mon, 14 Feb 2022 22:32:59 +0100 Subject: [PATCH 5/5] fix(storybook): make clickable optionnal and manage ngContent --- .../src/lib/badge/badge.component.stories.ts | 20 ++++++++++++++----- .../widgets/src/lib/badge/badge.component.ts | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/libs/ui/widgets/src/lib/badge/badge.component.stories.ts b/libs/ui/widgets/src/lib/badge/badge.component.stories.ts index a7a9808d8a..ecf9a8b7d4 100644 --- a/libs/ui/widgets/src/lib/badge/badge.component.stories.ts +++ b/libs/ui/widgets/src/lib/badge/badge.component.stories.ts @@ -1,19 +1,28 @@ -import { Meta, moduleMetadata, Story } from '@storybook/angular' +import { + componentWrapperDecorator, + Meta, + moduleMetadata, + Story, +} from '@storybook/angular' import { BadgeComponent } from './badge.component' export default { title: 'Widgets/BadgeComponent', component: BadgeComponent, decorators: [ + componentWrapperDecorator(BadgeComponent), moduleMetadata({ imports: [], }), ], } as Meta -type BadgeComponentWithContent = { content: string } - -const Template: Story = (args: BadgeComponent) => ({ +interface BadgeComponentContent extends Partial { + content: string +} +const Template: Story = ( + args: BadgeComponentContent +) => ({ component: BadgeComponent, props: args, template: '{{content}}', @@ -21,5 +30,6 @@ const Template: Story = (args: BadgeComponent) => ({ export const Primary = Template.bind({}) Primary.args = { - content: 'My badge!', + clickable: true, + content: 'My custom badge', } diff --git a/libs/ui/widgets/src/lib/badge/badge.component.ts b/libs/ui/widgets/src/lib/badge/badge.component.ts index 88f4053e36..8d78ac1b34 100644 --- a/libs/ui/widgets/src/lib/badge/badge.component.ts +++ b/libs/ui/widgets/src/lib/badge/badge.component.ts @@ -7,5 +7,5 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' changeDetection: ChangeDetectionStrategy.OnPush, }) export class BadgeComponent { - @Input() clickable = false + @Input() clickable? = false }