From 1c2ba16c12e48ac4d61f5f9199c4fd7a565985a7 Mon Sep 17 00:00:00 2001 From: Nick Troshkov Date: Thu, 18 Sep 2025 17:43:46 +0200 Subject: [PATCH 1/2] fix(Table): cursor: pointer for clickable headers --- src/components/Table/Table.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx index 962d583b..f1639957 100644 --- a/src/components/Table/Table.tsx +++ b/src/components/Table/Table.tsx @@ -34,11 +34,13 @@ const StyledHeader = styled.th<{ $size: TableSize }>` text-align: left; `; -const HeaderContentWrapper = styled.div` +const HeaderContentWrapper = styled.div<{ $interactive: boolean }>` display: flex; align-items: center; justify-content: start; gap: inherit; + + ${({ $interactive }) => $interactive && "cursor: pointer;"} `; const SortIcon = styled(Icon)<{ $sortDir: SortDir }>` @@ -57,6 +59,9 @@ const TableHeader = ({ ...delegated }: Omit & { onSort?: () => void; size: TableSize }) => { const isSorted = typeof sortDir === "string"; + const isInteractive = Boolean( + typeof onClick === "function" || (isSortable && typeof onSort === "function") + ); const onHeaderClick = (e: MouseEvent): void => { if (typeof onClick === "function") { onClick(e); @@ -70,7 +75,10 @@ const TableHeader = ({ $size={size} {...delegated} > - + {isSorted && isSortable && sortPosition == "start" && ( Date: Thu, 18 Sep 2025 17:44:03 +0200 Subject: [PATCH 2/2] docs(Table): add story with sortable table --- src/components/Table/Table.stories.tsx | 43 +++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/components/Table/Table.stories.tsx b/src/components/Table/Table.stories.tsx index c6e3a6b6..69b9f27b 100644 --- a/src/components/Table/Table.stories.tsx +++ b/src/components/Table/Table.stories.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { Meta, StoryObj } from "@storybook/react-vite"; @@ -94,3 +94,44 @@ export const Selectable: StoryObj = { ); }, }; + +export const Sortable: StoryObj = { + args: { + headers, + rows, + }, + render: ({ rows, headers, ...props }) => { + const [sort, setSort] = useState<[number, "asc" | "desc"]>([0, "asc"]); + + const sortedHeaders = useMemo( + () => + headers.map((header, headerIndex) => ({ + ...header, + isSortable: true, + sortDir: sort[0] === headerIndex ? sort[1] : undefined, + })), + [headers, sort] + ); + + const sortedRows = useMemo( + () => + [...rows].sort((a, b) => { + const [cellIdx, sortDir] = sort; + const cellA = a.items[cellIdx]?.label?.toString() || ""; + const cellB = b.items[cellIdx]?.label?.toString() || ""; + const result = cellA.localeCompare(cellB, "en", { numeric: true }); + return sortDir === "asc" ? result : -result; + }), + [rows, sort] + ); + + return ( + void setSort([idx, dir])} + /> + ); + }, +};