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"