diff --git a/packages/calcite-components/src/components/filter/filter.e2e.ts b/packages/calcite-components/src/components/filter/filter.e2e.ts index aec61bf27f3..03a0e6c996b 100644 --- a/packages/calcite-components/src/components/filter/filter.e2e.ts +++ b/packages/calcite-components/src/components/filter/filter.e2e.ts @@ -272,6 +272,52 @@ describe("calcite-filter", () => { }); }); + describe("filter method", () => { + let page: E2EPage; + + beforeEach(async () => { + page = await newE2EPage(); + await page.setContent(``); + await page.evaluate(() => { + const filter = document.querySelector("calcite-filter"); + filter.items = [ + { + name: "Harry", + description: "developer", + value: "harry", + metadata: { haircolor: "red", favoriteBand: "MetallicA" } + }, + { + name: "Matt", + description: "developer", + value: "matt", + metadata: { haircolor: "black", favoriteBand: "Radiohead" } + } + ]; + }); + }); + + it("should filter with value argument", async () => { + const filter = await page.find("calcite-filter"); + const filterChangeSpy = await page.spyOnEvent("calciteFilterChange"); + await filter.callMethod("filter", "Matt"); + await page.waitForChanges(); + expect(filterChangeSpy).toHaveReceivedEventTimes(0); + assertMatchingItems(await filter.getProperty("filteredItems"), ["matt"]); + }); + + it("should filter without value argument", async () => { + const filter = await page.find("calcite-filter"); + filter.setProperty("value", "harry"); + await page.waitForChanges(); + const filterChangeSpy = await page.spyOnEvent("calciteFilterChange"); + await filter.callMethod("filter"); + await page.waitForChanges(); + expect(filterChangeSpy).toHaveReceivedEventTimes(0); + assertMatchingItems(await filter.getProperty("filteredItems"), ["harry"]); + }); + }); + describe("translation support", () => { t9n("calcite-filter"); }); diff --git a/packages/calcite-components/src/components/filter/filter.tsx b/packages/calcite-components/src/components/filter/filter.tsx index 882c55b1742..1f154adc990 100644 --- a/packages/calcite-components/src/components/filter/filter.tsx +++ b/packages/calcite-components/src/components/filter/filter.tsx @@ -61,7 +61,7 @@ export class Filter @Watch("items") watchItemsHandler(): void { - this.filter(this.value); + this.filterDebounced(this.value); } /** @@ -112,7 +112,7 @@ export class Filter @Watch("value") valueHandler(value: string): void { - this.filter(value); + this.filterDebounced(value); } // -------------------------------------------------------------------------- @@ -169,6 +169,7 @@ export class Filter disconnectedCallback(): void { disconnectLocalized(this); disconnectMessages(this); + this.filterDebounced.cancel(); } componentDidLoad(): void { @@ -181,6 +182,22 @@ export class Filter // // -------------------------------------------------------------------------- + /** + * Performs a filter on the component. + * + * This method can be useful because filtering is delayed and asynchronous. + * + * @param {string} value - The filter text value. + * @returns {Promise} + */ + @Method() + async filter(value: string = this.value): Promise { + return new Promise((resolve) => { + this.value = value; + this.filterDebounced(value, false, resolve); + }); + } + /** Sets focus on the component. */ @Method() async setFocus(): Promise { @@ -195,15 +212,16 @@ export class Filter // // -------------------------------------------------------------------------- - private filter = debounce( - (value: string, emit = false): void => this.updateFiltered(filter(this.items, value), emit), + private filterDebounced = debounce( + (value: string, emit = false, onFilter?: () => void): void => + this.updateFiltered(filter(this.items, value), emit, onFilter), DEBOUNCE_TIMEOUT ); inputHandler = (event: CustomEvent): void => { const target = event.target as HTMLCalciteInputElement; this.value = target.value; - this.filter(target.value, true); + this.filterDebounced(target.value, true); }; keyDownHandler = (event: KeyboardEvent): void => { @@ -219,16 +237,16 @@ export class Filter clear = (): void => { this.value = ""; - this.filter("", true); + this.filterDebounced("", true); this.setFocus(); }; - updateFiltered(filtered: any[], emit = false): void { - this.filteredItems.length = 0; - this.filteredItems = this.filteredItems.concat(filtered); + updateFiltered(filtered: object[], emit = false, callback?: () => void): void { + this.filteredItems = filtered; if (emit) { this.calciteFilterChange.emit(); } + callback?.(); } // --------------------------------------------------------------------------