Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(table, table-row): Improve programmatic Table Row selection behavior #11237

Merged
merged 7 commits into from
Jan 9, 2025
Original file line number Diff line number Diff line change
@@ -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 });

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

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

override updated(): void {
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
@@ -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", () => {
11 changes: 9 additions & 2 deletions packages/calcite-components/src/components/table/table.tsx
Original file line number Diff line number Diff line change
@@ -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<TableRowFocusEvent>): void {
const cellPosition = event.detail.cellPosition;
const rowPos = event.detail.rowPosition;
97 changes: 97 additions & 0 deletions packages/calcite-components/src/demos/table.html
Original file line number Diff line number Diff line change
@@ -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">
@@ -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>
Loading