From 21073fe9138c578ff3c28b2a2d659a54cb050b9c Mon Sep 17 00:00:00 2001 From: Matt Driscoll Date: Wed, 18 Sep 2024 10:28:16 -0700 Subject: [PATCH 1/6] fix(list, filter): fix sync between list items and filtered data. #10230 --- .../calcite-components/src/components.d.ts | 100 ++++++++++++++++++ .../src/components/filter/filter.tsx | 7 +- .../src/components/list/list.tsx | 30 +++--- 3 files changed, 120 insertions(+), 17 deletions(-) diff --git a/packages/calcite-components/src/components.d.ts b/packages/calcite-components/src/components.d.ts index bffc8ef3637..68d4f03e98e 100644 --- a/packages/calcite-components/src/components.d.ts +++ b/packages/calcite-components/src/components.d.ts @@ -9,91 +9,191 @@ import { Alignment, Appearance, CollapseDirection, FlipContext, IconType, Kind, import { RequestedItem } from "./components/accordion/interfaces"; import { IconNameOrString } from "./components/icon/interfaces"; import { RequestedItem as RequestedItem1 } from "./components/accordion-item/interfaces"; +import { ActionMessages } from "./components/action/assets/action/t9n"; import { FlipPlacement, LogicalPlacement, MenuPlacement, OverlayPositioning, ReferenceElement } from "./utils/floating-ui"; +import { ActionBarMessages } from "./components/action-bar/assets/action-bar/t9n"; import { Columns } from "./components/action-group/interfaces"; +import { ActionGroupMessages } from "./components/action-group/assets/action-group/t9n"; +import { ActionPadMessages } from "./components/action-pad/assets/action-pad/t9n"; import { AlertDuration, AlertQueue } from "./components/alert/interfaces"; import { NumberingSystem } from "./utils/locale"; +import { AlertMessages } from "./components/alert/assets/alert/t9n"; import { HeadingLevel } from "./components/functional/Heading"; +import { BlockMessages } from "./components/block/assets/block/t9n"; import { BlockSectionToggleDisplay } from "./components/block-section/interfaces"; +import { BlockSectionMessages } from "./components/block-section/assets/block-section/t9n"; import { ButtonAlignment, DropdownIconType } from "./components/button/interfaces"; +import { ButtonMessages } from "./components/button/assets/button/t9n"; +import { CardMessages } from "./components/card/assets/card/t9n"; import { ArrowType, AutoplayType } from "./components/carousel/interfaces"; +import { CarouselMessages } from "./components/carousel/assets/carousel/t9n"; import { MutableValidityState } from "./utils/form"; +import { ChipMessages } from "./components/chip/assets/chip/t9n"; import { ColorValue, InternalColor } from "./components/color-picker/interfaces"; import { Format } from "./components/color-picker/utils"; +import { ColorPickerMessages } from "./components/color-picker/assets/color-picker/t9n"; import { ComboboxChildElement, SelectionDisplay } from "./components/combobox/interfaces"; +import { ComboboxMessages } from "./components/combobox/assets/combobox/t9n"; +import { DatePickerMessages } from "./components/date-picker/assets/date-picker/t9n"; import { DateLocaleData } from "./components/date-picker/utils"; import { HoverRange } from "./utils/date"; +import { DialogMessages } from "./components/dialog/assets/dialog/t9n"; import { DialogPlacement } from "./components/dialog/interfaces"; import { RequestedItem as RequestedItem2 } from "./components/dropdown-group/interfaces"; import { ItemKeyboardEvent } from "./components/dropdown/interfaces"; +import { FilterMessages } from "./components/filter/assets/filter/t9n"; import { FlowItemLikeElement } from "./components/flow/interfaces"; +import { FlowItemMessages } from "./components/flow-item/assets/flow-item/t9n"; import { ColorStop, DataSeries } from "./components/graph/interfaces"; +import { HandleMessages } from "./components/handle/assets/handle/t9n"; import { HandleChange, HandleNudge } from "./components/handle/interfaces"; +import { InlineEditableMessages } from "./components/inline-editable/assets/inline-editable/t9n"; import { InputPlacement } from "./components/input/interfaces"; +import { InputMessages } from "./components/input/assets/input/t9n"; +import { InputDatePickerMessages } from "./components/input-date-picker/assets/input-date-picker/t9n"; +import { InputNumberMessages } from "./components/input-number/assets/input-number/t9n"; +import { InputTextMessages } from "./components/input-text/assets/input-text/t9n"; +import { InputTimePickerMessages } from "./components/input-time-picker/assets/input-time-picker/t9n"; +import { TimePickerMessages } from "./components/time-picker/assets/time-picker/t9n"; +import { InputTimeZoneMessages } from "./components/input-time-zone/assets/input-time-zone/t9n"; import { OffsetStyle, TimeZoneMode } from "./components/input-time-zone/interfaces"; import { ListDragDetail } from "./components/list/interfaces"; import { ItemData } from "./components/list-item/interfaces"; +import { ListMessages } from "./components/list/assets/list/t9n"; import { SelectionAppearance } from "./components/list/resources"; +import { ListItemMessages } from "./components/list-item/assets/list-item/t9n"; +import { MenuMessages } from "./components/menu/assets/menu/t9n"; +import { MenuItemMessages } from "./components/menu-item/assets/menu-item/t9n"; import { MenuItemCustomEvent } from "./components/menu-item/interfaces"; import { MeterFillType, MeterLabelType } from "./components/meter/interfaces"; +import { ModalMessages } from "./components/modal/assets/modal/t9n"; +import { NoticeMessages } from "./components/notice/assets/notice/t9n"; +import { PaginationMessages } from "./components/pagination/assets/pagination/t9n"; +import { PanelMessages } from "./components/panel/assets/panel/t9n"; import { ItemData as ItemData1, ListFocusId } from "./components/pick-list/shared-list-logic"; import { ICON_TYPES } from "./components/pick-list/resources"; +import { PickListItemMessages } from "./components/pick-list-item/assets/pick-list-item/t9n"; +import { PopoverMessages } from "./components/popover/assets/popover/t9n"; +import { RatingMessages } from "./components/rating/assets/rating/t9n"; +import { ScrimMessages } from "./components/scrim/assets/scrim/t9n"; import { DisplayMode } from "./components/sheet/interfaces"; import { DisplayMode as DisplayMode1 } from "./components/shell-panel/interfaces"; +import { ShellPanelMessages } from "./components/shell-panel/assets/shell-panel/t9n"; import { DragDetail } from "./utils/sortableComponent"; import { StepperItemChangeEventDetail, StepperItemEventDetail, StepperItemKeyEventDetail, StepperLayout } from "./components/stepper/interfaces"; +import { StepperMessages } from "./components/stepper/assets/stepper/t9n"; +import { StepperItemMessages } from "./components/stepper-item/assets/stepper-item/t9n"; import { TabID, TabLayout, TabPosition } from "./components/tabs/interfaces"; +import { TabNavMessages } from "./components/tab-nav/assets/tab-nav/t9n"; import { Element } from "@stencil/core"; import { TabChangeEventDetail, TabCloseEventDetail } from "./components/tab/interfaces"; +import { TabTitleMessages } from "./components/tab-title/assets/tab-title/t9n"; import { RowType, TableInteractionMode, TableLayout, TableRowFocusEvent, TableSelectionDisplay } from "./components/table/interfaces"; +import { TableMessages } from "./components/table/assets/table/t9n"; +import { TableCellMessages } from "./components/table-cell/assets/table-cell/t9n"; +import { TableHeaderMessages } from "./components/table-header/assets/table-header/t9n"; +import { TextAreaMessages } from "./components/text-area/assets/text-area/t9n"; import { TileSelectType } from "./components/tile-select/interfaces"; import { TileSelectGroupLayout } from "./components/tile-select-group/interfaces"; +import { TipMessages } from "./components/tip/assets/tip/t9n"; +import { TipManagerMessages } from "./components/tip-manager/assets/tip-manager/t9n"; import { TreeItemSelectDetail } from "./components/tree-item/interfaces"; +import { ValueListMessages } from "./components/value-list/assets/value-list/t9n"; import { ListItemAndHandle } from "./components/value-list-item/interfaces"; export { Alignment, Appearance, CollapseDirection, FlipContext, IconType, Kind, Layout, LogicalFlowPosition, Position, Scale, SelectionAppearance as SelectionAppearance1, SelectionMode, Status, Width } from "./components/interfaces"; export { RequestedItem } from "./components/accordion/interfaces"; export { IconNameOrString } from "./components/icon/interfaces"; export { RequestedItem as RequestedItem1 } from "./components/accordion-item/interfaces"; +export { ActionMessages } from "./components/action/assets/action/t9n"; export { FlipPlacement, LogicalPlacement, MenuPlacement, OverlayPositioning, ReferenceElement } from "./utils/floating-ui"; +export { ActionBarMessages } from "./components/action-bar/assets/action-bar/t9n"; export { Columns } from "./components/action-group/interfaces"; +export { ActionGroupMessages } from "./components/action-group/assets/action-group/t9n"; +export { ActionPadMessages } from "./components/action-pad/assets/action-pad/t9n"; export { AlertDuration, AlertQueue } from "./components/alert/interfaces"; export { NumberingSystem } from "./utils/locale"; +export { AlertMessages } from "./components/alert/assets/alert/t9n"; export { HeadingLevel } from "./components/functional/Heading"; +export { BlockMessages } from "./components/block/assets/block/t9n"; export { BlockSectionToggleDisplay } from "./components/block-section/interfaces"; +export { BlockSectionMessages } from "./components/block-section/assets/block-section/t9n"; export { ButtonAlignment, DropdownIconType } from "./components/button/interfaces"; +export { ButtonMessages } from "./components/button/assets/button/t9n"; +export { CardMessages } from "./components/card/assets/card/t9n"; export { ArrowType, AutoplayType } from "./components/carousel/interfaces"; +export { CarouselMessages } from "./components/carousel/assets/carousel/t9n"; export { MutableValidityState } from "./utils/form"; +export { ChipMessages } from "./components/chip/assets/chip/t9n"; export { ColorValue, InternalColor } from "./components/color-picker/interfaces"; export { Format } from "./components/color-picker/utils"; +export { ColorPickerMessages } from "./components/color-picker/assets/color-picker/t9n"; export { ComboboxChildElement, SelectionDisplay } from "./components/combobox/interfaces"; +export { ComboboxMessages } from "./components/combobox/assets/combobox/t9n"; +export { DatePickerMessages } from "./components/date-picker/assets/date-picker/t9n"; export { DateLocaleData } from "./components/date-picker/utils"; export { HoverRange } from "./utils/date"; +export { DialogMessages } from "./components/dialog/assets/dialog/t9n"; export { DialogPlacement } from "./components/dialog/interfaces"; export { RequestedItem as RequestedItem2 } from "./components/dropdown-group/interfaces"; export { ItemKeyboardEvent } from "./components/dropdown/interfaces"; +export { FilterMessages } from "./components/filter/assets/filter/t9n"; export { FlowItemLikeElement } from "./components/flow/interfaces"; +export { FlowItemMessages } from "./components/flow-item/assets/flow-item/t9n"; export { ColorStop, DataSeries } from "./components/graph/interfaces"; +export { HandleMessages } from "./components/handle/assets/handle/t9n"; export { HandleChange, HandleNudge } from "./components/handle/interfaces"; +export { InlineEditableMessages } from "./components/inline-editable/assets/inline-editable/t9n"; export { InputPlacement } from "./components/input/interfaces"; +export { InputMessages } from "./components/input/assets/input/t9n"; +export { InputDatePickerMessages } from "./components/input-date-picker/assets/input-date-picker/t9n"; +export { InputNumberMessages } from "./components/input-number/assets/input-number/t9n"; +export { InputTextMessages } from "./components/input-text/assets/input-text/t9n"; +export { InputTimePickerMessages } from "./components/input-time-picker/assets/input-time-picker/t9n"; +export { TimePickerMessages } from "./components/time-picker/assets/time-picker/t9n"; +export { InputTimeZoneMessages } from "./components/input-time-zone/assets/input-time-zone/t9n"; export { OffsetStyle, TimeZoneMode } from "./components/input-time-zone/interfaces"; export { ListDragDetail } from "./components/list/interfaces"; export { ItemData } from "./components/list-item/interfaces"; +export { ListMessages } from "./components/list/assets/list/t9n"; export { SelectionAppearance } from "./components/list/resources"; +export { ListItemMessages } from "./components/list-item/assets/list-item/t9n"; +export { MenuMessages } from "./components/menu/assets/menu/t9n"; +export { MenuItemMessages } from "./components/menu-item/assets/menu-item/t9n"; export { MenuItemCustomEvent } from "./components/menu-item/interfaces"; export { MeterFillType, MeterLabelType } from "./components/meter/interfaces"; +export { ModalMessages } from "./components/modal/assets/modal/t9n"; +export { NoticeMessages } from "./components/notice/assets/notice/t9n"; +export { PaginationMessages } from "./components/pagination/assets/pagination/t9n"; +export { PanelMessages } from "./components/panel/assets/panel/t9n"; export { ItemData as ItemData1, ListFocusId } from "./components/pick-list/shared-list-logic"; export { ICON_TYPES } from "./components/pick-list/resources"; +export { PickListItemMessages } from "./components/pick-list-item/assets/pick-list-item/t9n"; +export { PopoverMessages } from "./components/popover/assets/popover/t9n"; +export { RatingMessages } from "./components/rating/assets/rating/t9n"; +export { ScrimMessages } from "./components/scrim/assets/scrim/t9n"; export { DisplayMode } from "./components/sheet/interfaces"; export { DisplayMode as DisplayMode1 } from "./components/shell-panel/interfaces"; +export { ShellPanelMessages } from "./components/shell-panel/assets/shell-panel/t9n"; export { DragDetail } from "./utils/sortableComponent"; export { StepperItemChangeEventDetail, StepperItemEventDetail, StepperItemKeyEventDetail, StepperLayout } from "./components/stepper/interfaces"; +export { StepperMessages } from "./components/stepper/assets/stepper/t9n"; +export { StepperItemMessages } from "./components/stepper-item/assets/stepper-item/t9n"; export { TabID, TabLayout, TabPosition } from "./components/tabs/interfaces"; +export { TabNavMessages } from "./components/tab-nav/assets/tab-nav/t9n"; export { Element } from "@stencil/core"; export { TabChangeEventDetail, TabCloseEventDetail } from "./components/tab/interfaces"; +export { TabTitleMessages } from "./components/tab-title/assets/tab-title/t9n"; export { RowType, TableInteractionMode, TableLayout, TableRowFocusEvent, TableSelectionDisplay } from "./components/table/interfaces"; +export { TableMessages } from "./components/table/assets/table/t9n"; +export { TableCellMessages } from "./components/table-cell/assets/table-cell/t9n"; +export { TableHeaderMessages } from "./components/table-header/assets/table-header/t9n"; +export { TextAreaMessages } from "./components/text-area/assets/text-area/t9n"; export { TileSelectType } from "./components/tile-select/interfaces"; export { TileSelectGroupLayout } from "./components/tile-select-group/interfaces"; +export { TipMessages } from "./components/tip/assets/tip/t9n"; +export { TipManagerMessages } from "./components/tip-manager/assets/tip-manager/t9n"; export { TreeItemSelectDetail } from "./components/tree-item/interfaces"; +export { ValueListMessages } from "./components/value-list/assets/value-list/t9n"; export { ListItemAndHandle } from "./components/value-list-item/interfaces"; export namespace Components { interface CalciteAccordion { 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.tsx b/packages/calcite-components/src/components/list/list.tsx index f329e36f215..37d5987a6d8 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(false, true); } //-------------------------------------------------------------------------- @@ -409,7 +409,7 @@ export class List connectLocalized(this); connectMessages(this); this.connectObserver(); - this.updateListItems(); + this.updateListItems(false, true); this.setUpSorting(); this.setParentList(); } @@ -471,7 +471,7 @@ export class List listItems: HTMLCalciteListItemElement[] = []; - mutationObserver = createObserver("mutation", () => this.updateListItems()); + mutationObserver = createObserver("mutation", () => this.updateListItems(false, true)); visibleItems: HTMLCalciteListItemElement[] = []; @@ -810,7 +810,12 @@ export class List this.updateListItems(emit); } - private async performFilter(): Promise { + private async filterAndUpdateData(): Promise { + await this.filterEl?.filter(this.filterText); + this.updateFilteredData(); + } + + private performFilter(): void { const { filterEl, filterText, filterProps } = this; if (!filterEl) { @@ -819,8 +824,7 @@ export class List filterEl.value = filterText; filterEl.filterProps = filterProps; - await filterEl.filter(filterText); - this.updateFilteredData(); + this.filterAndUpdateData(); } private setFilterEl = (el: HTMLCalciteFilterElement): void => { @@ -844,8 +848,8 @@ export class List })); }; - private updateListItems = debounce((emitFilterChange = false): void => { - const { selectionAppearance, selectionMode, dragEnabled, el } = this; + private updateListItems = debounce((emitFilterChange = false, filter = false): void => { + const { selectionAppearance, selectionMode, dragEnabled, el, filterEl, filterEnabled } = this; const items = Array.from(this.el.querySelectorAll(listItemSelector)); @@ -863,10 +867,12 @@ export class List } this.listItems = items; - if (this.filterEnabled) { + if (filterEnabled && filter) { 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); From 6f395678403e32dac1117ba7e4e2db1d84372df8 Mon Sep 17 00:00:00 2001 From: Matt Driscoll Date: Wed, 18 Sep 2024 11:40:30 -0700 Subject: [PATCH 2/6] cleanup --- .../src/components/list/list.tsx | 87 +++++++++++-------- 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/packages/calcite-components/src/components/list/list.tsx b/packages/calcite-components/src/components/list/list.tsx index 37d5987a6d8..6f1c217af8d 100755 --- a/packages/calcite-components/src/components/list/list.tsx +++ b/packages/calcite-components/src/components/list/list.tsx @@ -232,7 +232,7 @@ export class List @Watch("selectionMode") @Watch("selectionAppearance") handleListItemChange(): void { - this.updateListItems(false, true); + this.updateListItems({ emitFilterChange: false, performFilter: true }); } //-------------------------------------------------------------------------- @@ -391,7 +391,7 @@ export class List } event.stopPropagation(); - this.updateListItems(); + this.updateListItems({ emitFilterChange: false, performFilter: false }); } @Listen("calciteInternalListItemGroupDefaultSlotChange") @@ -409,7 +409,7 @@ export class List connectLocalized(this); connectMessages(this); this.connectObserver(); - this.updateListItems(false, true); + this.updateListItems({ emitFilterChange: false, performFilter: true }); this.setUpSorting(); this.setParentList(); } @@ -471,7 +471,9 @@ export class List listItems: HTMLCalciteListItemElement[] = []; - mutationObserver = createObserver("mutation", () => this.updateListItems(false, true)); + mutationObserver = createObserver("mutation", () => + this.updateListItems({ emitFilterChange: false, performFilter: true }), + ); visibleItems: HTMLCalciteListItemElement[] = []; @@ -671,7 +673,7 @@ export class List onDragSort(detail: ListDragDetail): void { this.setParentList(); - this.updateListItems(); + this.updateListItems({ emitFilterChange: false, performFilter: false }); this.calciteListOrderChange.emit(detail); } @@ -807,7 +809,7 @@ export class List this.filteredData = filterEl.filteredItems as ItemData; } - this.updateListItems(emit); + this.updateListItems({ emitFilterChange: emit, performFilter: false }); } private async filterAndUpdateData(): Promise { @@ -848,41 +850,50 @@ export class List })); }; - private updateListItems = debounce((emitFilterChange = false, filter = false): void => { - const { selectionAppearance, selectionMode, dragEnabled, el, filterEl, filterEnabled } = this; - - 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; + private updateListItems = debounce( + ({ + emitFilterChange = false, + performFilter = false, + }: { + emitFilterChange: boolean; + performFilter: boolean; + }): void => { + const { selectionAppearance, selectionMode, dragEnabled, el, filterEl, filterEnabled } = this; + + 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; + } + }); + + if (this.parentListEl) { + this.setUpSorting(); + return; } - }); - - if (this.parentListEl) { - this.setUpSorting(); - return; - } - this.listItems = items; - if (filterEnabled && filter) { - this.dataForFilter = this.getItemData(); + this.listItems = items; + if (filterEnabled && performFilter) { + this.dataForFilter = this.getItemData(); - if (filterEl) { - filterEl.items = this.dataForFilter; - this.filterAndUpdateData(); + 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; @@ -998,7 +1009,7 @@ export class List parentEl.insertBefore(dragEl, referenceEl); - this.updateListItems(); + this.updateListItems({ emitFilterChange: false, performFilter: false }); this.connectObserver(); this.calciteListOrderChange.emit({ From f4abe6ea1264d3b613dcc471ccd2fd449a4381a8 Mon Sep 17 00:00:00 2001 From: Matt Driscoll Date: Wed, 18 Sep 2024 16:46:24 -0700 Subject: [PATCH 3/6] add test --- .../src/components/list/list.e2e.ts | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/packages/calcite-components/src/components/list/list.e2e.ts b/packages/calcite-components/src/components/list/list.e2e.ts index 95e2df2fbd5..33bcd52b355 100755 --- a/packages/calcite-components/src/components/list/list.e2e.ts +++ b/packages/calcite-components/src/components/list/list.e2e.ts @@ -441,6 +441,61 @@ 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 listItems = await page.findAll("calcite-list-item"); + + expect(listItems).toHaveLength(3); + listItems.forEach(async (item) => { + expect(await item.getProperty("value")).toBe("list1"); + }); + expect(await listItems[0].getProperty("filterHidden")).toBe(false); + expect(await listItems[1].getProperty("filterHidden")).toBe(false); + expect(await listItems[2].getProperty("filterHidden")).toBe(false); + + list.setProperty("filterText", matchingFont); + await page.waitForChanges(); + await page.waitForTimeout(DEBOUNCE.filter); + + expect(listItems).toHaveLength(3); + listItems.forEach(async (item) => { + expect(await item.getProperty("value")).toBe("list1"); + }); + expect(await listItems[0].getProperty("filterHidden")).toBe(false); + expect(await listItems[1].getProperty("filterHidden")).toBe(false); + expect(await listItems[2].getProperty("filterHidden")).toBe(true); + + list.innerHTML = html` + + + + `; + await page.waitForChanges(); + await page.waitForTimeout(DEBOUNCE.filter); + + expect(await list.getProperty("filterText")).toBe(matchingFont); + listItems = await page.findAll("calcite-list-item"); + expect(listItems).toHaveLength(3); + listItems.forEach(async (item) => { + expect(await item.getProperty("value")).toBe("list2"); + }); + expect(await listItems[0].getProperty("filterHidden")).toBe(false); + expect(await listItems[1].getProperty("filterHidden")).toBe(false); + expect(await listItems[2].getProperty("filterHidden")).toBe(true); + }); + it("filters initially", async () => { const page = await newE2EPage(); await page.setContent(html` From eee1082ba2415993c9647d49b70e3b8efa099293 Mon Sep 17 00:00:00 2001 From: Matt Driscoll Date: Thu, 19 Sep 2024 08:56:03 -0700 Subject: [PATCH 4/6] fix test --- .../src/components/list/list.e2e.ts | 44 ++++++++----------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/packages/calcite-components/src/components/list/list.e2e.ts b/packages/calcite-components/src/components/list/list.e2e.ts index 33bcd52b355..6ff0683defd 100755 --- a/packages/calcite-components/src/components/list/list.e2e.ts +++ b/packages/calcite-components/src/components/list/list.e2e.ts @@ -447,53 +447,45 @@ describe("calcite-list", () => { const page = await newE2EPage(); await page.setContent(html` - - - + + + `); await page.waitForChanges(); const list = await page.find("calcite-list"); - let listItems = await page.findAll("calcite-list-item"); + let visibleItems = await page.findAll("calcite-list-item:not([filter-hidden])"); - expect(listItems).toHaveLength(3); - listItems.forEach(async (item) => { - expect(await item.getProperty("value")).toBe("list1"); + expect(visibleItems).toHaveLength(3); + visibleItems.forEach(async (item) => { + expect(await item.getProperty("description")).toBe("list1"); }); - expect(await listItems[0].getProperty("filterHidden")).toBe(false); - expect(await listItems[1].getProperty("filterHidden")).toBe(false); - expect(await listItems[2].getProperty("filterHidden")).toBe(false); list.setProperty("filterText", matchingFont); await page.waitForChanges(); await page.waitForTimeout(DEBOUNCE.filter); - expect(listItems).toHaveLength(3); - listItems.forEach(async (item) => { - expect(await item.getProperty("value")).toBe("list1"); + 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"); }); - expect(await listItems[0].getProperty("filterHidden")).toBe(false); - expect(await listItems[1].getProperty("filterHidden")).toBe(false); - expect(await listItems[2].getProperty("filterHidden")).toBe(true); list.innerHTML = html` - - - + + + `; await page.waitForChanges(); await page.waitForTimeout(DEBOUNCE.filter); expect(await list.getProperty("filterText")).toBe(matchingFont); - listItems = await page.findAll("calcite-list-item"); - expect(listItems).toHaveLength(3); - listItems.forEach(async (item) => { - expect(await item.getProperty("value")).toBe("list2"); + 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"); }); - expect(await listItems[0].getProperty("filterHidden")).toBe(false); - expect(await listItems[1].getProperty("filterHidden")).toBe(false); - expect(await listItems[2].getProperty("filterHidden")).toBe(true); }); it("filters initially", async () => { From 499875ea0a206e67ad7f53237546b0bf00bb7cde Mon Sep 17 00:00:00 2001 From: Matt Driscoll Date: Wed, 25 Sep 2024 13:45:05 -0700 Subject: [PATCH 5/6] partial review fixes --- .../src/components/list/list.tsx | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/packages/calcite-components/src/components/list/list.tsx b/packages/calcite-components/src/components/list/list.tsx index 6f1c217af8d..52a7f43dea2 100755 --- a/packages/calcite-components/src/components/list/list.tsx +++ b/packages/calcite-components/src/components/list/list.tsx @@ -232,7 +232,7 @@ export class List @Watch("selectionMode") @Watch("selectionAppearance") handleListItemChange(): void { - this.updateListItems({ emitFilterChange: false, performFilter: true }); + this.updateListItems({ performFilter: true }); } //-------------------------------------------------------------------------- @@ -391,7 +391,7 @@ export class List } event.stopPropagation(); - this.updateListItems({ emitFilterChange: false, performFilter: false }); + this.updateListItems(); } @Listen("calciteInternalListItemGroupDefaultSlotChange") @@ -409,7 +409,7 @@ export class List connectLocalized(this); connectMessages(this); this.connectObserver(); - this.updateListItems({ emitFilterChange: false, performFilter: true }); + this.updateListItems({ performFilter: true }); this.setUpSorting(); this.setParentList(); } @@ -472,7 +472,7 @@ export class List listItems: HTMLCalciteListItemElement[] = []; mutationObserver = createObserver("mutation", () => - this.updateListItems({ emitFilterChange: false, performFilter: true }), + this.updateListItems({ performFilter: true }), ); visibleItems: HTMLCalciteListItemElement[] = []; @@ -673,7 +673,7 @@ export class List onDragSort(detail: ListDragDetail): void { this.setParentList(); - this.updateListItems({ emitFilterChange: false, performFilter: false }); + this.updateListItems(); this.calciteListOrderChange.emit(detail); } @@ -809,7 +809,7 @@ export class List this.filteredData = filterEl.filteredItems as ItemData; } - this.updateListItems({ emitFilterChange: emit, performFilter: false }); + this.updateListItems({ emitFilterChange: emit }); } private async filterAndUpdateData(): Promise { @@ -851,13 +851,10 @@ export class List }; private updateListItems = debounce( - ({ - emitFilterChange = false, - performFilter = false, - }: { - emitFilterChange: boolean; - performFilter: boolean; - }): void => { + (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)); @@ -1009,7 +1006,7 @@ export class List parentEl.insertBefore(dragEl, referenceEl); - this.updateListItems({ emitFilterChange: false, performFilter: false }); + this.updateListItems(); this.connectObserver(); this.calciteListOrderChange.emit({ From a41ef7617110f00b1f2e57a3dbb0afd750f07c82 Mon Sep 17 00:00:00 2001 From: Matt Driscoll Date: Wed, 25 Sep 2024 15:01:04 -0700 Subject: [PATCH 6/6] add test --- .../src/components/filter/filter.e2e.ts | 8 ++++++++ 1 file changed, 8 insertions(+) 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 () => {