diff --git a/packages/calcite-components/src/components/filter/filter.e2e.ts b/packages/calcite-components/src/components/filter/filter.e2e.ts index a0b9e9c5b7a..fc90fb5311c 100644 --- a/packages/calcite-components/src/components/filter/filter.e2e.ts +++ b/packages/calcite-components/src/components/filter/filter.e2e.ts @@ -222,6 +222,14 @@ describe("calcite-filter", () => { assertMatchingItems(await filter.getProperty("filteredItems"), ["jon"]); expect(filterChangeSpy).toHaveReceivedEventTimes(1); + + await page.$eval("calcite-filter", (filter: HTMLCalciteFilterElement): void => { + filter.items = []; + }); + await page.waitForTimeout(DEBOUNCE.filter); + await page.waitForChanges(); + assertMatchingItems(await filter.getProperty("filteredItems"), []); + expect(filterChangeSpy).toHaveReceivedEventTimes(1); }); it("searches recursively in items and works and matches on a partial string ignoring case", async () => { diff --git a/packages/calcite-components/src/components/filter/filter.tsx b/packages/calcite-components/src/components/filter/filter.tsx index ba26a158e8f..3900e735204 100644 --- a/packages/calcite-components/src/components/filter/filter.tsx +++ b/packages/calcite-components/src/components/filter/filter.tsx @@ -167,9 +167,7 @@ export class Filter async componentWillLoad(): Promise { setUpLoadableComponent(this); - if (this.items.length) { - this.updateFiltered(filter(this.items, this.value, this.filterProps)); - } + this.updateFiltered(filter(this.items ?? [], this.value, this.filterProps)); await setUpMessages(this); } @@ -230,8 +228,7 @@ export class Filter private filterDebounced = debounce( (value: string, emit = false, onFilter?: () => void): void => - this.items.length && - this.updateFiltered(filter(this.items, value, this.filterProps), emit, onFilter), + this.updateFiltered(filter(this.items ?? [], value, this.filterProps), emit, onFilter), DEBOUNCE.filter, ); diff --git a/packages/calcite-components/src/components/list/list.e2e.ts b/packages/calcite-components/src/components/list/list.e2e.ts index 95e2df2fbd5..6ff0683defd 100755 --- a/packages/calcite-components/src/components/list/list.e2e.ts +++ b/packages/calcite-components/src/components/list/list.e2e.ts @@ -441,6 +441,53 @@ describe("calcite-list", () => { expect(selectedItemValues[1]).toBe("three"); }); + it("updating items after filtering", async () => { + const matchingFont = "Courier"; + + const page = await newE2EPage(); + await page.setContent(html` + + + + + + `); + await page.waitForChanges(); + + const list = await page.find("calcite-list"); + let visibleItems = await page.findAll("calcite-list-item:not([filter-hidden])"); + + expect(visibleItems).toHaveLength(3); + visibleItems.forEach(async (item) => { + expect(await item.getProperty("description")).toBe("list1"); + }); + + list.setProperty("filterText", matchingFont); + await page.waitForChanges(); + await page.waitForTimeout(DEBOUNCE.filter); + + visibleItems = await page.findAll("calcite-list-item:not([filter-hidden])"); + expect(visibleItems).toHaveLength(2); + visibleItems.forEach(async (item) => { + expect(await item.getProperty("description")).toBe("list1"); + }); + + list.innerHTML = html` + + + + `; + await page.waitForChanges(); + await page.waitForTimeout(DEBOUNCE.filter); + + expect(await list.getProperty("filterText")).toBe(matchingFont); + visibleItems = await page.findAll("calcite-list-item:not([filter-hidden])"); + expect(visibleItems).toHaveLength(2); + visibleItems.forEach(async (item) => { + expect(await item.getProperty("description")).toBe("list2"); + }); + }); + it("filters initially", async () => { const page = await newE2EPage(); await page.setContent(html` diff --git a/packages/calcite-components/src/components/list/list.tsx b/packages/calcite-components/src/components/list/list.tsx index f329e36f215..52a7f43dea2 100755 --- a/packages/calcite-components/src/components/list/list.tsx +++ b/packages/calcite-components/src/components/list/list.tsx @@ -163,7 +163,7 @@ export class List @Prop() filterProps: string[]; @Watch("filterProps") - async handlefilterPropsChange(): Promise { + async handleFilterPropsChange(): Promise { this.performFilter(); } @@ -232,7 +232,7 @@ export class List @Watch("selectionMode") @Watch("selectionAppearance") handleListItemChange(): void { - this.updateListItems(); + this.updateListItems({ performFilter: true }); } //-------------------------------------------------------------------------- @@ -409,7 +409,7 @@ export class List connectLocalized(this); connectMessages(this); this.connectObserver(); - this.updateListItems(); + this.updateListItems({ performFilter: true }); this.setUpSorting(); this.setParentList(); } @@ -471,7 +471,9 @@ export class List listItems: HTMLCalciteListItemElement[] = []; - mutationObserver = createObserver("mutation", () => this.updateListItems()); + mutationObserver = createObserver("mutation", () => + this.updateListItems({ performFilter: true }), + ); visibleItems: HTMLCalciteListItemElement[] = []; @@ -807,10 +809,15 @@ export class List this.filteredData = filterEl.filteredItems as ItemData; } - this.updateListItems(emit); + this.updateListItems({ emitFilterChange: emit }); + } + + private async filterAndUpdateData(): Promise { + await this.filterEl?.filter(this.filterText); + this.updateFilteredData(); } - private async performFilter(): Promise { + private performFilter(): void { const { filterEl, filterText, filterProps } = this; if (!filterEl) { @@ -819,8 +826,7 @@ export class List filterEl.value = filterText; filterEl.filterProps = filterProps; - await filterEl.filter(filterText); - this.updateFilteredData(); + this.filterAndUpdateData(); } private setFilterEl = (el: HTMLCalciteFilterElement): void => { @@ -844,39 +850,47 @@ export class List })); }; - private updateListItems = debounce((emitFilterChange = false): void => { - const { selectionAppearance, selectionMode, dragEnabled, el } = this; + private updateListItems = debounce( + (options?: { emitFilterChange?: boolean; performFilter?: boolean }): void => { + const emitFilterChange = options?.emitFilterChange ?? false; + const performFilter = options?.performFilter ?? false; + + const { selectionAppearance, selectionMode, dragEnabled, el, filterEl, filterEnabled } = this; + + const items = Array.from(this.el.querySelectorAll(listItemSelector)); - const items = Array.from(this.el.querySelectorAll(listItemSelector)); + items.forEach((item) => { + item.selectionAppearance = selectionAppearance; + item.selectionMode = selectionMode; + if (item.closest("calcite-list") === el) { + item.dragHandle = dragEnabled; + } + }); - items.forEach((item) => { - item.selectionAppearance = selectionAppearance; - item.selectionMode = selectionMode; - if (item.closest("calcite-list") === el) { - item.dragHandle = dragEnabled; + if (this.parentListEl) { + this.setUpSorting(); + return; } - }); - if (this.parentListEl) { - this.setUpSorting(); - return; - } + this.listItems = items; + if (filterEnabled && performFilter) { + this.dataForFilter = this.getItemData(); - this.listItems = items; - if (this.filterEnabled) { - this.dataForFilter = this.getItemData(); - if (this.filterEl) { - this.filterEl.items = this.dataForFilter; + if (filterEl) { + filterEl.items = this.dataForFilter; + this.filterAndUpdateData(); + } } - } - this.visibleItems = this.listItems.filter((item) => !item.closed && !item.hidden); - this.updateFilteredItems(emitFilterChange); - this.borderItems(); - this.focusableItems = this.filteredItems.filter((item) => !item.disabled); - this.setActiveListItem(); - this.updateSelectedItems(); - this.setUpSorting(); - }, debounceTimeout); + this.visibleItems = this.listItems.filter((item) => !item.closed && !item.hidden); + this.updateFilteredItems(emitFilterChange); + this.borderItems(); + this.focusableItems = this.filteredItems.filter((item) => !item.disabled); + this.setActiveListItem(); + this.updateSelectedItems(); + this.setUpSorting(); + }, + debounceTimeout, + ); private focusRow = (focusEl: HTMLCalciteListItemElement): void => { const { focusableItems } = this;