Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
"tsc-alias": "1.8.10",
"typescript": "5.7.3",
"typescript-eslint": "8.24.1",
"vanilla-framework": "4.39.0",
"vanilla-framework": "4.41.0",
"wait-on": "8.0.2",
"webpack": "5.98.0"
},
Expand Down
64 changes: 60 additions & 4 deletions src/components/MainTable/MainTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,9 @@ describe("MainTable", () => {
);

await userEvent.click(
screen.getByRole("columnheader", { name: "Status" }),
within(screen.getByRole("columnheader", { name: "Status" })).getByRole(
"button",
),
);
// The status should now be ascending.
expect(within(rowItems[1]).getByRole("rowheader").textContent).toBe(
Expand All @@ -213,7 +215,9 @@ describe("MainTable", () => {
);

await userEvent.click(
screen.getByRole("columnheader", { name: "Status" }),
within(screen.getByRole("columnheader", { name: "Status" })).getByRole(
"button",
),
);
// The status should now be descending.
expect(within(rowItems[1]).getByRole("rowheader").textContent).toBe(
Expand All @@ -229,7 +233,9 @@ describe("MainTable", () => {
);

await userEvent.click(
screen.getByRole("columnheader", { name: "Status" }),
within(screen.getByRole("columnheader", { name: "Status" })).getByRole(
"button",
),
);
// The status be back to the original order.
expect(within(rowItems[1]).getByRole("rowheader").textContent).toBe(
Expand All @@ -249,7 +255,9 @@ describe("MainTable", () => {
render(<MainTable headers={headers} rows={rows} sortable={true} />);
const rowItems = screen.getAllByRole("row");
await userEvent.click(
screen.getByRole("columnheader", { name: "Status" }),
within(screen.getByRole("columnheader", { name: "Status" })).getByRole(
"button",
),
);

const expectedOrder = ["Idle", "Ready", "Waiting"];
Expand All @@ -260,6 +268,14 @@ describe("MainTable", () => {
);
}

// Non-sortable columns don't have sort buttons.
expect(
within(screen.getByRole("columnheader", { name: "RAM" })).queryByRole(
"button",
),
).not.toBeInTheDocument();

// Clicking a column header, not a sort button.
await userEvent.click(screen.getByRole("columnheader", { name: "RAM" }));
// The status should not change
for (let i = 1; i < 4; i++) {
Expand All @@ -277,6 +293,46 @@ describe("MainTable", () => {
}
});

it("can be sorted with the keyboard", async () => {
render(<MainTable headers={headers} rows={rows} sortable={true} />);
const rowItems = screen.getAllByRole("row");
// Check the initial status order.
expect(within(rowItems[1]).getByRole("rowheader").textContent).toBe(
"Ready",
);

expect(within(rowItems[2]).getByRole("rowheader").textContent).toBe(
"Waiting",
);

expect(within(rowItems[3]).getByRole("rowheader").textContent).toBe(
"Idle",
);

await userEvent.tab();
expect(
within(screen.getByRole("columnheader", { name: "Status" })).getByRole(
"button",
),
).toHaveFocus();
await userEvent.keyboard("{Enter}");
// The status should now be ascending.
expect(within(rowItems[1]).getByRole("rowheader").textContent).toBe(
"Idle",
);
// You should also be able to sort with the Spacebar.
await userEvent.keyboard(" ");
// The status should now be descending.
expect(within(rowItems[1]).getByRole("rowheader").textContent).toBe(
"Waiting",
);
await userEvent.keyboard("{Enter}");
// The status be back to the original order.
expect(within(rowItems[1]).getByRole("rowheader").textContent).toBe(
"Ready",
);
});

it("can set a default sort", () => {
render(
<MainTable
Expand Down
2 changes: 1 addition & 1 deletion src/components/MainTable/MainTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ const generateHeaders = (
<TableHeader
key={index}
sort={sortDirection}
onClick={
onSort={
sortable && sortKey
? updateSort.bind(
this,
Expand Down
14 changes: 13 additions & 1 deletion src/components/TableHeader/TableHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,30 @@ export type Props = PropsWithSpread<
* The direction of sorting, if applicable.
*/
sort?: SortDirection;
/** Function to call when the sort button is clicked. */
onSort?: () => void;
},
HTMLProps<HTMLTableHeaderCellElement>
>;

const TableHeader = ({
children,
sort,
onSort,
...props
}: Props): React.JSX.Element => {
const headerContents = () =>
sort && onSort ? (
<button className="p-table__sort-button" onClick={onSort}>
{children}
</button>
) : (
children
);

return (
<th role="columnheader" aria-sort={sort} {...props}>
{children}
{headerContents()}
</th>
);
};
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13090,10 +13090,10 @@ validate-npm-package-name@^7.0.0:
resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-7.0.0.tgz#3b4fe12b4abfb8b0be010d0e75b1fe2b52295bc6"
integrity sha512-bwVk/OK+Qu108aJcMAEiU4yavHUI7aN20TgZNBj9MR2iU1zPUl1Z1Otr7771ExfYTPTvfN8ZJ1pbr5Iklgt4xg==

vanilla-framework@4.39.0:
version "4.39.0"
resolved "https://registry.yarnpkg.com/vanilla-framework/-/vanilla-framework-4.39.0.tgz#d0c62c6cff11576cf4e1e950152559b6149e85ed"
integrity sha512-6j3k88OjxgNLWDG4GPumMvEy56ZwpVunKcZmglX2NdM6n+SjYNDd7o2ldVy2wEqeYMy8jUPo88Yq+LrruDNM+g==
vanilla-framework@4.41.0:
version "4.41.0"
resolved "https://registry.yarnpkg.com/vanilla-framework/-/vanilla-framework-4.41.0.tgz#bc981035c536bd6cca5b6f3ded2abad679231499"
integrity sha512-UMCb+1+kfSoMFmXwwfjR/oyliojQ6pjOsBYwSRN6HgNtL14KhQc0VECYnv4YlkNTAJqQLR7VFwq95aXJrSrJSw==

verror@1.10.0:
version "1.10.0"
Expand Down
Loading