diff --git a/package.json b/package.json
index 4f8f018bd..83472c0d9 100644
--- a/package.json
+++ b/package.json
@@ -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"
},
diff --git a/src/components/MainTable/MainTable.test.tsx b/src/components/MainTable/MainTable.test.tsx
index ff3a70292..f98b55993 100644
--- a/src/components/MainTable/MainTable.test.tsx
+++ b/src/components/MainTable/MainTable.test.tsx
@@ -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(
@@ -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(
@@ -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(
@@ -249,7 +255,9 @@ describe("MainTable", () => {
render();
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"];
@@ -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++) {
@@ -277,6 +293,46 @@ describe("MainTable", () => {
}
});
+ it("can be sorted with the keyboard", async () => {
+ render();
+ 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(
void;
},
HTMLProps
>;
@@ -19,11 +21,21 @@ export type Props = PropsWithSpread<
const TableHeader = ({
children,
sort,
+ onSort,
...props
}: Props): React.JSX.Element => {
+ const headerContents = () =>
+ sort && onSort ? (
+
+ ) : (
+ children
+ );
+
return (
|
- {children}
+ {headerContents()}
|
);
};
diff --git a/yarn.lock b/yarn.lock
index 6c669dcc0..d846147ce 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"