From 29116752a20bebc92068487f28ecc4896aff8a1a Mon Sep 17 00:00:00 2001 From: Adam Tirella Date: Wed, 8 Jan 2025 16:54:20 -0800 Subject: [PATCH] fix(table, table-row): Improve programmatic Table Row selection behavior (#11237) **Related Issue:** #11142 ## Summary Adds a private event that allows us to update the Table's `selectedItems` and displayed selection UI when Table Rows are programmatically selected or de-selected, without emitting a public event. Adds local demo and test. Also updates an incorrect private function name within the Table component. --- .../src/components/table-row/table-row.tsx | 7 ++ .../src/components/table/table.e2e.ts | 78 +++++++++++++++ .../src/components/table/table.tsx | 11 ++- .../calcite-components/src/demos/table.html | 97 +++++++++++++++++++ 4 files changed, 191 insertions(+), 2 deletions(-) diff --git a/packages/calcite-components/src/components/table-row/table-row.tsx b/packages/calcite-components/src/components/table-row/table-row.tsx index 458fff4ebc9..2eadc8ff9c5 100644 --- a/packages/calcite-components/src/components/table-row/table-row.tsx +++ b/packages/calcite-components/src/components/table-row/table-row.tsx @@ -104,6 +104,9 @@ export class TableRow extends LitElement implements InteractiveComponent { /** @private */ calciteInternalTableRowFocusRequest = createEvent({ cancelable: false }); + /** @private */ + calciteInternalTableRowSelect = createEvent({ cancelable: false }); + /** Fires when the selected state of the component changes. */ calciteTableRowSelect = createEvent({ cancelable: false }); @@ -146,6 +149,10 @@ export class TableRow extends LitElement implements InteractiveComponent { ) { this.handleDelayedCellChanges(); } + + if (changes.has("selected")) { + this.calciteInternalTableRowSelect.emit(); + } } override updated(): void { diff --git a/packages/calcite-components/src/components/table/table.e2e.ts b/packages/calcite-components/src/components/table/table.e2e.ts index 6388e3c1652..66d262ecafa 100644 --- a/packages/calcite-components/src/components/table/table.e2e.ts +++ b/packages/calcite-components/src/components/table/table.e2e.ts @@ -1131,6 +1131,84 @@ describe("selection modes", () => { expect(await element.getProperty("selectedItems")).toHaveLength(1); await selectedItemAsserter([row1.id]); }); + + it("correctly updates selected items and does not emit public event when table row selected properties are programmatically set", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + + + cell + cell + + + cell + cell + + + cell + cell + + `, + ); + + const selectedItemAsserter = await createSelectedItemsAsserter(page, "calcite-table", "calciteTableSelect"); + + const element = await page.find("calcite-table"); + const row1 = await page.find("#row-1"); + const row2 = await page.find("#row-2"); + const row3 = await page.find("#row-3"); + + const tableSelectSpy = await element.spyOnEvent("calciteTableSelect"); + await page.waitForChanges(); + + expect(await row1.getProperty("selected")).toBe(true); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await selectedItemAsserter([row1.id]); + + row1.setProperty("selected", false); + await page.waitForChanges(); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toHaveLength(0); + await selectedItemAsserter([]); + + row2.setProperty("selected", true); + await page.waitForChanges(); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(true); + expect(await row3.getProperty("selected")).toBe(false); + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toHaveLength(1); + await selectedItemAsserter([row2.id]); + + row3.setProperty("selected", true); + await page.waitForChanges(); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(true); + expect(await row3.getProperty("selected")).toBe(true); + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toHaveLength(2); + await selectedItemAsserter([row2.id, row3.id]); + + row2.setProperty("selected", false); + row3.setProperty("selected", false); + await page.waitForChanges(); + expect(await row1.getProperty("selected")).toBe(false); + expect(await row2.getProperty("selected")).toBe(false); + expect(await row3.getProperty("selected")).toBe(false); + expect(tableSelectSpy).toHaveReceivedEventTimes(0); + expect(await element.getProperty("selectedItems")).toHaveLength(0); + await selectedItemAsserter([]); + }); }); describe("pagination event", () => { diff --git a/packages/calcite-components/src/components/table/table.tsx b/packages/calcite-components/src/components/table/table.tsx index 70646900ba0..188893e36b5 100644 --- a/packages/calcite-components/src/components/table/table.tsx +++ b/packages/calcite-components/src/components/table/table.tsx @@ -171,7 +171,8 @@ export class Table extends LitElement implements LoadableComponent { constructor() { super(); - this.listen("calciteTableRowSelect", this.calciteChipSelectListener); + this.listen("calciteTableRowSelect", this.calciteTableRowSelectListener); + this.listen("calciteInternalTableRowSelect", this.calciteInternalTableRowSelectListener); this.listen("calciteInternalTableRowFocusRequest", this.calciteInternalTableRowFocusEvent); } @@ -212,12 +213,18 @@ export class Table extends LitElement implements LoadableComponent { this.updateRows(); } - private calciteChipSelectListener(event: CustomEvent): void { + private calciteTableRowSelectListener(event: CustomEvent): void { if (event.composedPath().includes(this.el)) { this.setSelectedItems(event.target as TableRow["el"]); } } + private calciteInternalTableRowSelectListener(event: CustomEvent): void { + if (event.composedPath().includes(this.el)) { + this.updateSelectedItems(false); + } + } + private calciteInternalTableRowFocusEvent(event: CustomEvent): void { const cellPosition = event.detail.cellPosition; const rowPos = event.detail.rowPosition; diff --git a/packages/calcite-components/src/demos/table.html b/packages/calcite-components/src/demos/table.html index c390b3e4cae..f5839c1284e 100644 --- a/packages/calcite-components/src/demos/table.html +++ b/packages/calcite-components/src/demos/table.html @@ -383,6 +383,90 @@ +

Programmatic row selection testing

+ + + Custom Deselect + + + + + + + + + + + + + cell content + cell content + cell content + cell content + + + cell content + cell content + cell content + cell content + + + cell content + cell content + cell content + cell content + + + cell content + cell content + cell content + cell content + + + cell content + cell content + cell content + cell content + + + cell content + cell content + cell content + cell content + + + cell content + cell content + cell content + cell content + + + cell content + cell content + cell content + cell content + + + cell content + cell content + cell content + cell content + + + cell content + cell content + cell content + cell content + +

Scroll in parent testing

@@ -4971,5 +5055,18 @@

} }); }); + + const programmaticButton = document.getElementById("programmatic-button"); + const programmaticTable = document.getElementById("programmatic-table"); + const programmaticTableRows = programmaticTable.querySelectorAll("calcite-table-row:not([slot='table-header'])"); + programmaticButton.addEventListener("click", () => { + programmaticTableRows.forEach((row) => { + row.selected = false; + }); + }); + + programmaticTable.addEventListener("calciteTableSelect", () => { + console.log(programmaticTable.selectedItems); + });