Skip to content

Commit

Permalink
fix(table, table-row): Improve programmatic Table Row selection behav…
Browse files Browse the repository at this point in the history
…ior (#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.
  • Loading branch information
macandcheese authored Jan 9, 2025
1 parent dd66952 commit 2911675
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ export class TableRow extends LitElement implements InteractiveComponent {
/** @private */
calciteInternalTableRowFocusRequest = createEvent<TableRowFocusEvent>({ cancelable: false });

/** @private */
calciteInternalTableRowSelect = createEvent({ cancelable: false });

/** Fires when the selected state of the component changes. */
calciteTableRowSelect = createEvent({ cancelable: false });

Expand Down Expand Up @@ -146,6 +149,10 @@ export class TableRow extends LitElement implements InteractiveComponent {
) {
this.handleDelayedCellChanges();
}

if (changes.has("selected")) {
this.calciteInternalTableRowSelect.emit();
}
}

override updated(): void {
Expand Down
78 changes: 78 additions & 0 deletions packages/calcite-components/src/components/table/table.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`<calcite-table selection-mode="multiple" caption="Simple table" page-size="2" style="width:50rem">
<calcite-table-row id="row-head" slot="${SLOTS.tableHeader}">
<calcite-table-header heading="Heading" description="Description"></calcite-table-header>
<calcite-table-header heading="Heading" description="Description"></calcite-table-header>
</calcite-table-row>
<calcite-table-row id="row-1" selected>
<calcite-table-cell>cell</calcite-table-cell>
<calcite-table-cell>cell</calcite-table-cell>
</calcite-table-row>
<calcite-table-row id="row-2">
<calcite-table-cell>cell</calcite-table-cell>
<calcite-table-cell>cell</calcite-table-cell>
</calcite-table-row>
<calcite-table-row id="row-3">
<calcite-table-cell>cell</calcite-table-cell>
<calcite-table-cell>cell</calcite-table-cell>
</calcite-table-row>
</calcite-table>`,
);

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", () => {
Expand Down
11 changes: 9 additions & 2 deletions packages/calcite-components/src/components/table/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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<TableRowFocusEvent>): void {
const cellPosition = event.detail.cellPosition;
const rowPos = event.detail.rowPosition;
Expand Down
97 changes: 97 additions & 0 deletions packages/calcite-components/src/demos/table.html
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,90 @@
</calcite-table>
</div>
</calcite-modal>
<h3>Programmatic row selection testing</h3>

<calcite-table
id="programmatic-table"
caption="Simple table"
striped
numbered
page-size="6"
bordered
selection-mode="multiple"
>
<calcite-button id="programmatic-button" slot="selection-actions">Custom Deselect</calcite-button>

<calcite-action-bar slot="selection-actions" layout="horizontal" expand-disabled>
<calcite-action text="Analyze" icon="layer"></calcite-action>
<calcite-action text="Copy" icon="copy"></calcite-action>
</calcite-action-bar>
<calcite-table-row slot="table-header">
<calcite-table-header heading="Heading" description="Description"></calcite-table-header>
<calcite-table-header heading="Heading" description="Description"></calcite-table-header>
<calcite-table-header heading="Heading" description="Description"></calcite-table-header>
<calcite-table-header heading="Heading" description="Description"></calcite-table-header>
</calcite-table-row>
<calcite-table-row>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
</calcite-table-row>
<calcite-table-row>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
</calcite-table-row>
<calcite-table-row>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
</calcite-table-row>
<calcite-table-row>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
</calcite-table-row>
<calcite-table-row>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
</calcite-table-row>
<calcite-table-row selected>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
</calcite-table-row>
<calcite-table-row selected>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
</calcite-table-row>
<calcite-table-row>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
</calcite-table-row>
<calcite-table-row>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
</calcite-table-row>
<calcite-table-row>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
<calcite-table-cell>cell content</calcite-table-cell>
</calcite-table-row>
</calcite-table>

<h3>Scroll in parent testing</h3>
<div style="display: inline-flex; flex-direction: row; gap: 1rem; margin: 0 auto; text-align: center">
Expand Down Expand Up @@ -4971,5 +5055,18 @@ <h3>
}
});
});

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);
});
</script>
</html>

0 comments on commit 2911675

Please sign in to comment.