diff --git a/packages/components/filter-bar/__screenshots__/01-dark.png b/packages/components/filter-bar/__screenshots__/01-dark.png index 15c8733ad..0f2002c34 100644 Binary files a/packages/components/filter-bar/__screenshots__/01-dark.png and b/packages/components/filter-bar/__screenshots__/01-dark.png differ diff --git a/packages/components/filter-bar/__screenshots__/01-light.png b/packages/components/filter-bar/__screenshots__/01-light.png index 5cde626eb..1d9fa4ff3 100644 Binary files a/packages/components/filter-bar/__screenshots__/01-light.png and b/packages/components/filter-bar/__screenshots__/01-light.png differ diff --git a/packages/components/filter-bar/pipes/pipe-select.spec.ts b/packages/components/filter-bar/pipes/pipe-select.spec.ts index deecb41b9..1a334eadc 100644 --- a/packages/components/filter-bar/pipes/pipe-select.spec.ts +++ b/packages/components/filter-bar/pipes/pipe-select.spec.ts @@ -438,6 +438,45 @@ describe('KbqPipeSelectComponent', () => { })); }); + describe('late pipeTemplates (first-open options)', () => { + beforeEach(() => { + fixture = TestBed.createComponent(TestComponent); + filterBarDebugElement = fixture.debugElement.query(By.directive(KbqFilterBar)); + }); + + it('should render options on first open when templates are supplied after the pipe is created', fakeAsync(() => { + // Reproduce a parent assigning `pipeTemplates` in ngAfterViewInit: the Select pipe is + // created before its template (carrying the option `values`) arrives. + fixture.componentInstance.pipeTemplates = []; + fixture.componentInstance.activeFilter = createFilter([ + createPipe({ name: 'test', value: null, search: true }) + ]); + fixture.detectChanges(); + + // Templates arrive after the pipe is already initialized. + fixture.componentInstance.pipeTemplates = [ + { + name: 'Select', + id: PIPE_TEMPLATE_ID, + type: KbqPipeTypes.Select, + values: SELECT_VALUES, + cleanable: false, + removable: false, + disabled: false + } + ]; + fixture.detectChanges(); + + openSelect(); + flush(); + fixture.detectChanges(); + + const options = document.querySelectorAll('.kbq-option'); + + expect(options.length).toBe(SELECT_VALUES.length); + })); + }); + describe('onClear', () => { beforeEach(() => { fixture = TestBed.createComponent(TestComponent); diff --git a/packages/components/filter-bar/pipes/pipe-select.ts b/packages/components/filter-bar/pipes/pipe-select.ts index 424a02b6a..fd62479aa 100644 --- a/packages/components/filter-bar/pipes/pipe-select.ts +++ b/packages/components/filter-bar/pipes/pipe-select.ts @@ -8,7 +8,7 @@ import { KbqIcon } from '@koobiq/components/icon'; import { KbqInputModule } from '@koobiq/components/input'; import { KbqSelect, KbqSelectModule } from '@koobiq/components/select'; import { KbqTitleModule } from '@koobiq/components/title'; -import { merge, Observable, of } from 'rxjs'; +import { merge, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { KbqSelectValue } from '../filter-bar.types'; import { KbqBasePipe, KbqPipeMinWidth } from './base-pipe'; @@ -63,9 +63,12 @@ export class KbqPipeSelectComponent extends KbqBasePipe implemen /** @docs-private */ ngOnInit(): void { - this.filteredOptions = merge( - of(this.values), - this.searchControl.valueChanges.pipe(map((value) => this.getFilteredOptions(value))) + // Merge the live template stream (re-emits when `pipeTemplates` changes after init) with the + // search input, so options render on first open even when templates are supplied late + // (e.g. a parent assigning `pipeTemplates` in ngAfterViewInit for a viewChild valueTemplate). + this.filteredOptions = merge(this.filterBar!.internalTemplatesChanges, this.searchControl.valueChanges).pipe( + map(this.getFilteredOptions), + takeUntilDestroyed(this.destroyRef) ); } @@ -94,9 +97,11 @@ export class KbqPipeSelectComponent extends KbqBasePipe implemen this.select().open(); } - private getFilteredOptions(value: string | null): KbqSelectValue[] { - return value - ? this.values.filter((item: KbqSelectValue) => item.name.toLowerCase().includes(value.toLowerCase())) + private getFilteredOptions = (): KbqSelectValue[] => { + const search = this.searchControl.value; + + return search + ? this.values.filter((item: KbqSelectValue) => item.name.toLowerCase().includes(search.toLowerCase())) : this.values; - } + }; }