Skip to content

Commit

Permalink
fix(list, filter): fix sync between list items and filtered data (#10342
Browse files Browse the repository at this point in the history
)

**Related Issue:** #10230

## Summary

- updates Filter to emit `calciteFilterChange` when there are no
`filteredItems`
- Previously, when text was typed into the filter component but there
were no `items`, no event would be emitted.
- The doc says `Fires when the filter text changes.`. However, this was
not always the case if items were not present.
- This was problematic because you wouldn't get an event for filter text
changing unless there were items populated.
- If items were added later after a text input, you wouldn't have the
correct text to filter against.
-  updates the list to filter when list items are slotted in
- add test
  • Loading branch information
driskull authored Sep 25, 2024
1 parent 2d19268 commit 9a66601
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
7 changes: 2 additions & 5 deletions packages/calcite-components/src/components/filter/filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,7 @@ export class Filter

async componentWillLoad(): Promise<void> {
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);
}

Expand Down Expand Up @@ -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,
);

Expand Down
47 changes: 47 additions & 0 deletions packages/calcite-components/src/components/list/list.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`
<calcite-list filter-enabled filter-text="">
<calcite-list-item value="item1" label="${matchingFont}" description="list1"></calcite-list-item>
<calcite-list-item value="item2" label="${matchingFont} 2" description="list1"></calcite-list-item>
<calcite-list-item value="item3" label="Other Font" description="list1"></calcite-list-item>
</calcite-list>
`);
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`
<calcite-list-item value="item4" label="${matchingFont}" description="list2"></calcite-list-item>
<calcite-list-item value="item5" label="${matchingFont} 2" description="list2"></calcite-list-item>
<calcite-list-item value="item6" label="Other Font" description="list2"></calcite-list-item>
`;
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`
Expand Down
84 changes: 49 additions & 35 deletions packages/calcite-components/src/components/list/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export class List
@Prop() filterProps: string[];

@Watch("filterProps")
async handlefilterPropsChange(): Promise<void> {
async handleFilterPropsChange(): Promise<void> {
this.performFilter();
}

Expand Down Expand Up @@ -232,7 +232,7 @@ export class List
@Watch("selectionMode")
@Watch("selectionAppearance")
handleListItemChange(): void {
this.updateListItems();
this.updateListItems({ performFilter: true });
}

//--------------------------------------------------------------------------
Expand Down Expand Up @@ -409,7 +409,7 @@ export class List
connectLocalized(this);
connectMessages(this);
this.connectObserver();
this.updateListItems();
this.updateListItems({ performFilter: true });
this.setUpSorting();
this.setParentList();
}
Expand Down Expand Up @@ -471,7 +471,9 @@ export class List

listItems: HTMLCalciteListItemElement[] = [];

mutationObserver = createObserver("mutation", () => this.updateListItems());
mutationObserver = createObserver("mutation", () =>
this.updateListItems({ performFilter: true }),
);

visibleItems: HTMLCalciteListItemElement[] = [];

Expand Down Expand Up @@ -807,10 +809,15 @@ export class List
this.filteredData = filterEl.filteredItems as ItemData;
}

this.updateListItems(emit);
this.updateListItems({ emitFilterChange: emit });
}

private async filterAndUpdateData(): Promise<void> {
await this.filterEl?.filter(this.filterText);
this.updateFilteredData();
}

private async performFilter(): Promise<void> {
private performFilter(): void {
const { filterEl, filterText, filterProps } = this;

if (!filterEl) {
Expand All @@ -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 => {
Expand All @@ -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;
Expand Down

0 comments on commit 9a66601

Please sign in to comment.