From 05d8a70c6dca6ba74964f5c3e78825462e086c0b Mon Sep 17 00:00:00 2001 From: Joonatan Kuosa Date: Tue, 3 Sep 2024 14:22:51 +0300 Subject: [PATCH] refactor: remove custom forked hds table --- .../common/hds-fork/table/Table.module.scss | 192 ---------- .../src/common/hds-fork/table/Table.tsx | 338 ------------------ .../src/common/hds-fork/table/TableHeader.tsx | 67 ---- .../table/components/HeaderRow/HeaderRow.tsx | 11 - .../SortingHeaderCell/SortingHeaderCell.tsx | 118 ------ .../table/components/TableBody/TableBody.tsx | 25 -- .../TableContainer/TableContainer.tsx | 51 --- apps/admin-ui/src/component/Table.tsx | 75 ++-- .../[id]/review/AllocatedEventsTable.tsx | 1 - .../[id]/review/ApplicationEventsTable.tsx | 1 - .../[id]/review/ApplicationsTable.tsx | 1 - .../[id]/review/RejectedOccurrencesTable.tsx | 1 - 12 files changed, 46 insertions(+), 835 deletions(-) delete mode 100644 apps/admin-ui/src/common/hds-fork/table/Table.module.scss delete mode 100644 apps/admin-ui/src/common/hds-fork/table/Table.tsx delete mode 100644 apps/admin-ui/src/common/hds-fork/table/TableHeader.tsx delete mode 100644 apps/admin-ui/src/common/hds-fork/table/components/HeaderRow/HeaderRow.tsx delete mode 100644 apps/admin-ui/src/common/hds-fork/table/components/SortingHeaderCell/SortingHeaderCell.tsx delete mode 100644 apps/admin-ui/src/common/hds-fork/table/components/TableBody/TableBody.tsx delete mode 100644 apps/admin-ui/src/common/hds-fork/table/components/TableContainer/TableContainer.tsx diff --git a/apps/admin-ui/src/common/hds-fork/table/Table.module.scss b/apps/admin-ui/src/common/hds-fork/table/Table.module.scss deleted file mode 100644 index ee1afbc59..000000000 --- a/apps/admin-ui/src/common/hds-fork/table/Table.module.scss +++ /dev/null @@ -1,192 +0,0 @@ -@value x-small-down, small-down from "../breakpoints.css"; - -.table { - --content-background-color: var(--color-white); - composes: hds-table from 'hds-core/lib/components/table/table.css'; - - &.dark th { - color: var(--color-white); - } - - &.light th { - color: var(--color-black-90); - } - - .checkbox { - label { - padding-left: 0; - } - } -} - -.caption { - composes: hds-table__caption from 'hds-core/lib/components/table/table.css'; -} - -.container { - composes: hds-table-container from 'hds-core/lib/components/table/table.css'; - height: inherit; -} - -.headerRow { - composes: hds-table__header-row from 'hds-core/lib/components/table/table.css'; -} - -.content { - composes: hds-table__content from 'hds-core/lib/components/table/table.css'; -} - -.dark { - --header-background-color: var(--color-bus); - - .sortButton { - color: var(--color-white); - - &:focus { - outline: 3px solid var(--color-white); - } - } -} - -.light { - --header-background-color: var(--color-silver); - - .sortButton { - color: var(--color-black-90); - - &:focus { - outline: 3px solid var(--color-coat-of-arms); - } - } -} - -.dense { - composes: hds-table--dense from 'hds-core/lib/components/table/table.css'; - - .sortButton { - font-size: var(--fontsize-body-s); - font-weight: bold; - } - - .headerRow { - .sortingHeader { - padding-bottom: 7.5px !important; - padding-top: 7.5px !important; - } - } -} - -.zebra { - composes: hds-table--zebra from 'hds-core/lib/components/table/table.css'; -} - -.verticalLines { - composes: hds-table--with-vertical-lines from 'hds-core/lib/components/table/table.css'; -} - -.verticalHeaderColumn { - composes: hds-table__vertical-header-column from 'hds-core/lib/components/table/table.css'; -} - -.textAlignRight { - composes: hds-table--text-align-right from 'hds-core/lib/components/table/table.css'; -} - -.textAlignContentRight { - composes: hds-table__content--text-align-td-right from 'hds-core/lib/components/table/table.css'; -} - -.sortButton { - display: flex; - align-items: center; - justify-content: center; - padding: 0; - appearance: none; - background: transparent; - cursor: pointer; - border: 0; - height: 100%; - overflow: hidden; - font-weight: 500; - - .sortIcon { - margin-left: 10px; - } - - &:focus { - outline: 3px solid var(--color-white); - } -} - -/* MOBILE STYLES */ - -@media small-down { - .sortButton { - font-size: var(--fontsize-body-s); - font-weight: bold; - } - - .table { - .headerRow { - .sortingHeader { - padding-bottom: 7.5px; - padding-top: 7.5px; - } - } - } -} - -.sortColumnCell { - display: flex; - align-items: center; -} - -.checkboxHeader { - border-bottom: 1px solid var(--color-black-20); -} - -.checkboxData { - width: 24px; -} - -.actionContainer { - margin-bottom: var(--spacing-xs); - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: flex-end; - flex-wrap: wrap; - gap: var(--spacing-xs); - - .heading { - font-size: 24px; - font-weight: bold; - line-height: 29px; - } - - .actionButtonContainer { - display: flex; - gap: var(--spacing-2-xs); - flex-wrap: wrap; - } - - .actionButton { - flex-grow: 0; - } -} - -@media x-small-down { - .actionContainer { - .heading { - width: 100%; - } - - .actionButtonContainer { - width: 100%; - } - - .actionButton { - width: 100%; - } - } -} diff --git a/apps/admin-ui/src/common/hds-fork/table/Table.tsx b/apps/admin-ui/src/common/hds-fork/table/Table.tsx deleted file mode 100644 index 7c44148a3..000000000 --- a/apps/admin-ui/src/common/hds-fork/table/Table.tsx +++ /dev/null @@ -1,338 +0,0 @@ -import classNames from "classnames"; -import React from "react"; -import { HeaderRow } from "./components/HeaderRow/HeaderRow"; -import { SortingHeaderCell } from "./components/SortingHeaderCell/SortingHeaderCell"; -import { TableBody } from "./components/TableBody/TableBody"; -import { TableContainer } from "./components/TableContainer/TableContainer"; -import styles from "./Table.module.scss"; - -type Header = { - /** - * Boolean indicating whether a column is sortable - */ - isSortable?: boolean; - /** - * Key of header. Maps with the corresponding row data keys. - */ - key: string; - /** - * Visible header name that is rendered. - */ - headerName: string; - customSortCompareFunction?: (a: unknown, b: unknown) => number; - /** - * Sort icon type to be used in sorting. Use type string if the content is a string, otherwise use type other. - * @default 'string' - */ - sortIconType?: "string" | "other"; - /** - * Transform function for the corresponding row data. Use this to render custom content inside the table cell. - */ - transform?: ({ args }: any) => string | JSX.Element; // eslint-disable-line @typescript-eslint/no-explicit-any -}; - -interface TableCustomTheme { - /** - * Deprecated. Use --header-background-color instead. - */ - "--background-color"?: string; - /** - * Custom background color for table headers. - */ - "--header-background-color"?: string; - /** - * Custom background color for the table content. - * @default 'var(--color-white)' - */ - "--content-background-color"?: string; -} - -type SelectedRow = string | number; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Row = any; - -export type TableProps = React.ComponentPropsWithoutRef<"table"> & { - /** - * Aria-label for checkbox selection. - * @default 'Rivin valinta' - */ - ariaLabelCheckboxSelection?: string; - /** - * Aria-label for sort button in ascending state. - * @default 'Järjestetty nousevaan järjestykseen' - */ - ariaLabelSortButtonAscending?: string; - /** - * Aria-label for sort button in descending state. - * @default 'Järjestetty laskevaan järjestykseen' - */ - ariaLabelSortButtonDescending?: string; - /** - * Aria-label for sort button in the unordered state. - * @default '' - */ - ariaLabelSortButtonUnset?: string; - /** - * Caption of the table. - */ - caption?: string | React.ReactNode; - /** - * Boolean indicating whether the table has the checkbox selection column to select rows. - * @default false - */ - checkboxSelection?: boolean; - /** - * Text for clear selected rows button. - * @default 'Tyhjennä valinnat' - */ - clearSelectionsText?: string; - /** - * Columns of the table header row. Defines header name, optional sort icon type and optional cell row transform function. - */ - cols: Array
; - /** - * Custom action buttons to place on top of the table. - */ - customActionButtons?: React.ReactNode[]; - /** - * Test id attribute that is passed to the html table element. - * @default 'hds-table-data-testid' - */ - dataTestId?: string; - /** - * Boolean indicating whether to use the dense variant of the table. - * @default false - */ - dense?: boolean; - /** - * Table heading. - */ - heading?: string; - /** - * Table heading aria level. - */ - headingAriaLevel?: number; - /** - * A custom class name passed to table heading. - */ - headingClassName?: string; - /** - * Table heading id. Used to name table to assistive technologies. Only applicable when heading prop is used. - * @default 'hds-table-heading-id' - */ - headingId?: string; - /** - * Id that is passed to the native html table element. - * @default 'hds-table-id' - */ - id?: string; - /** - * Column key used as a unique identifier for a row - */ - indexKey: string; - /** - * Key indicating a column that you wish to be initially sorted. Use undefined to have no column initially sorted. - */ - initialSortingColumnKey?: string; - /** - * Sorting order applied for initial sorting. - */ - initialSortingOrder?: "asc" | "desc"; - /** - * Boolean indicating whether index column is rendered in the table. - * @default true - */ - renderIndexCol?: boolean; - /** - * Table rows. An array of objects where keys map with the keys of col. - */ - rows: Array; - /** - * Text for the select all rows button. - * @default 'Valitse kaikki rivit' - */ - selectAllRowsText?: string; - /** - * Selected table rows. - */ - selectedRows?: SelectedRow[]; - /** - * Callback that updates selected rows. - */ - setSelectedRows?: React.Dispatch>; - - setSort?: (col: string) => void; - /** - * Boolean indicating whether table data cell text content is aligned right. Default is false -> text is aligned left. - * @default false - */ - textAlignContentRight?: boolean; - /** - * Custom theme to change table header background color. - */ - theme?: TableCustomTheme; // Custom theme styles - /** - * Table variant. Use dark for dark brand background colors, and light for light brand background colors. - * @default 'dark' - */ - variant?: "dark" | "light"; - /** - * Vertical headers of the table. - */ - verticalHeaders?: Array
; - /** - * Boolean indicating whether the table has vertical lines on columns - */ - verticalLines?: boolean; - /** - * Boolean indicating whether the table has alternating row colors zebra style. - */ - zebra?: boolean; -}; - -export const Table = ({ - ariaLabelSortButtonAscending = "Järjestetty nousevaan järjestykseen", - ariaLabelSortButtonDescending = "Järjestetty laskevaan järjestykseen", - ariaLabelSortButtonUnset = "", - caption, - checkboxSelection = false, - cols, - customActionButtons, - dataTestId = "hds-table-data-testid", - dense = false, - heading, - headingAriaLevel = 2, - headingClassName, - headingId = "hds-table-heading-id", - id = "hds-table-id", - indexKey, - initialSortingColumnKey, - initialSortingOrder, - renderIndexCol = true, - rows, - textAlignContentRight = false, - theme, - variant = "dark", - verticalHeaders, - verticalLines = false, - zebra = false, - setSort = () => null, - ...rest -}: TableProps): JSX.Element => { - if (verticalHeaders && verticalHeaders.length && checkboxSelection) { - // eslint-disable-next-line no-console - console.warn( - "Incompatible props verticalHeaders and checkboxSelection provided. Cannot use checkboxSelection when verticalHeaders is applied" - ); - // eslint-disable-next-line no-param-reassign - checkboxSelection = false; - } - - if (checkboxSelection && caption) { - // eslint-disable-next-line no-console - console.warn( - "Cannot use caption prop when checkboxSelection is set to true. Use heading prop instead" - ); - } - - if (theme && theme["--background-color"]) { - // eslint-disable-next-line no-console - console.warn( - "--background-color is deprecated, and will be removed in a future release. Please use --header-background-color instead" - ); - - theme["--header-background-color"] = theme["--background-color"]; - delete theme["--background-color"]; - } - - const sorting = initialSortingColumnKey; - const order = initialSortingOrder; - - const hasCustomActionButtons = - customActionButtons && customActionButtons.length > 0; - - const visibleColumns = renderIndexCol - ? cols - : cols.filter((column) => column.key !== indexKey); - - return ( - <> - {(checkboxSelection || heading || hasCustomActionButtons) && ( -
- {heading && ( -
- {heading} -
- )} -
- )} - - - - {verticalHeaders && verticalHeaders.length && } - {visibleColumns.map((column) => { - if (column.isSortable) { - return ( - - ); - } - return ( - - {column.headerName} - - ); - })} - - - - {rows.map((row, index) => ( - - {verticalHeaders && verticalHeaders.length && ( - {verticalHeaders[index].headerName} - )} - {visibleColumns.map((column, cellIndex) => { - return ( - - {column.transform && column.transform(row)} - {!column.transform && row[column.key]} - - ); - })} - - ))} - - - - ); -}; diff --git a/apps/admin-ui/src/common/hds-fork/table/TableHeader.tsx b/apps/admin-ui/src/common/hds-fork/table/TableHeader.tsx deleted file mode 100644 index b9b71f31f..000000000 --- a/apps/admin-ui/src/common/hds-fork/table/TableHeader.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { HeaderRow } from "./components/HeaderRow/HeaderRow"; -import { SortingHeaderCell } from "./components/SortingHeaderCell/SortingHeaderCell"; - -type Props = { - indexKey: string; - cols: Header[]; - order: "desc" | "asc" | "unset"; - sorting: string; - setSortingAndOrder: (k: string) => void; -}; - -const TableHeader = ({ - indexKey, - cols, - sorting, - order, - setSortingAndOrder, -}: Props): JSX.Element => { - const { t } = useTranslation(); - const visibleColumns = cols.filter((column) => column.key !== indexKey); - return ( - - - - {visibleColumns.map((column) => { - if (column.isSortable) { - return ( - - ); - } - return ( - - ); - })} - - -
- {column.headerName} -
- ); -}; - -export default TableHeader; - -export type Header = { - isSortable?: boolean; - key: string; - headerName: string; - sortIconType?: "string" | "other"; - transform?: ({ args }: any) => string | JSX.Element; // eslint-disable-line @typescript-eslint/no-explicit-any -}; diff --git a/apps/admin-ui/src/common/hds-fork/table/components/HeaderRow/HeaderRow.tsx b/apps/admin-ui/src/common/hds-fork/table/components/HeaderRow/HeaderRow.tsx deleted file mode 100644 index a0b87ee6f..000000000 --- a/apps/admin-ui/src/common/hds-fork/table/components/HeaderRow/HeaderRow.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; - -import styles from "../../Table.module.scss"; - -export const HeaderRow = ({ - children, -}: { - children: React.ReactNode; -}): JSX.Element => { - return {children}; -}; diff --git a/apps/admin-ui/src/common/hds-fork/table/components/SortingHeaderCell/SortingHeaderCell.tsx b/apps/admin-ui/src/common/hds-fork/table/components/SortingHeaderCell/SortingHeaderCell.tsx deleted file mode 100644 index 714299984..000000000 --- a/apps/admin-ui/src/common/hds-fork/table/components/SortingHeaderCell/SortingHeaderCell.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import React from "react"; - -import { - IconSort, - IconSortAlphabeticalAscending, - IconSortAlphabeticalDescending, - IconSortAscending, - IconSortDescending, -} from "hds-react"; - -// import core base styles -import styles from "../../Table.module.scss"; - -type SortingHeaderCellProps = React.ComponentPropsWithoutRef<"th"> & { - ariaLabelSortButtonUnset: string; - ariaLabelSortButtonAscending: string; - ariaLabelSortButtonDescending: string; - colKey: string; - setSortingAndOrder: (colKey: string) => void; - order: "unset" | "asc" | "desc"; - title: string; - sortIconType: "string" | "other"; -}; - -type SortingIconProps = { - ariaLabelSortButtonUnset: string; - ariaLabelSortButtonAscending: string; - ariaLabelSortButtonDescending: string; - order: "unset" | "asc" | "desc"; - sortIconType: "string" | "other"; -}; - -const renderSortIcon = ({ - ariaLabelSortButtonUnset, - ariaLabelSortButtonAscending, - ariaLabelSortButtonDescending, - order, - sortIconType, -}: SortingIconProps) => { - if (order === "unset") { - return ( - - ); - } - if (order === "asc") { - if (sortIconType === "string") { - return ( - - ); - } - return ( - - ); - } - - if (sortIconType === "string") { - return ( - - ); - } - - return ( - - ); -}; - -export const SortingHeaderCell = ({ - ariaLabelSortButtonUnset, - ariaLabelSortButtonAscending, - ariaLabelSortButtonDescending, - colKey, - title, - setSortingAndOrder, - order = "unset", - sortIconType = "string", - ...rest -}: SortingHeaderCellProps): JSX.Element => { - return ( - -
- -
- - ); -}; diff --git a/apps/admin-ui/src/common/hds-fork/table/components/TableBody/TableBody.tsx b/apps/admin-ui/src/common/hds-fork/table/components/TableBody/TableBody.tsx deleted file mode 100644 index 69eb47be9..000000000 --- a/apps/admin-ui/src/common/hds-fork/table/components/TableBody/TableBody.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import classNames from "classnames"; -import React from "react"; - -import styles from "../../Table.module.scss"; - -type TableBodyProps = { - children: React.ReactNode; - textAlignContentRight?: boolean; -}; - -export const TableBody = ({ - children, - textAlignContentRight, -}: TableBodyProps): JSX.Element => { - return ( - - {children} - - ); -}; diff --git a/apps/admin-ui/src/common/hds-fork/table/components/TableContainer/TableContainer.tsx b/apps/admin-ui/src/common/hds-fork/table/components/TableContainer/TableContainer.tsx deleted file mode 100644 index 6825c959a..000000000 --- a/apps/admin-ui/src/common/hds-fork/table/components/TableContainer/TableContainer.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import classNames from "classnames"; -import React from "react"; - -import styles from "../../Table.module.scss"; - -type TableContainerProps = React.ComponentPropsWithoutRef<"table"> & { - children: React.ReactNode; - dataTestId?: string; - variant?: "dark" | "light"; - id: string; - dense?: boolean; - zebra?: boolean; - verticalLines?: boolean; - customThemeClass?: string; - headingId?: string; -}; - -export const TableContainer = ({ - children, - dataTestId, - variant = "dark", - id, - dense = false, - zebra = false, - verticalLines = false, - customThemeClass, - headingId, - ...rest -}: TableContainerProps): JSX.Element => { - return ( - // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex -
- - {children} -
-
- ); -}; diff --git a/apps/admin-ui/src/component/Table.tsx b/apps/admin-ui/src/component/Table.tsx index a03e148b1..61717e12a 100644 --- a/apps/admin-ui/src/component/Table.tsx +++ b/apps/admin-ui/src/component/Table.tsx @@ -2,10 +2,10 @@ import { get } from "lodash"; import React from "react"; import { Link } from "react-router-dom"; import styled from "styled-components"; -import { Table, TableProps } from "@/common/hds-fork/table/Table"; +import { Table, TableProps } from "hds-react"; +import { fontBold } from "common"; type TableWrapperProps = { - $headingBackground?: string; $tableBackground?: string; $colWidths?: string[]; }; @@ -23,16 +23,11 @@ const StyledTable = styled(Table)` border-collapse: collapse; th { text-align: left; - font-family: var(--font-bold); + ${fontBold}; padding: var(--spacing-xs); - background: ${({ $headingBackground = "var(--color-black-10)" }) => - $headingBackground}; position: sticky; top: 0; box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.4); - button { - color: black !important; - } } td { padding: var(--spacing-xs); @@ -47,37 +42,59 @@ const StyledTable = styled(Table)` } `; -type Props = TableProps & { +// Custom sort callback for backwards compatibility (can be refactored out) +type Props = Omit & { setSort?: (col: string) => void; isLoading?: boolean; - // hack to avoid circular key error - // TODO should fix the bug in the Table component - disableKey?: boolean; }; // @param isLoading - if true, table is rendered with a loading overlay // TODO overlay and spinner for loading would be preferable over colour switching -export const CustomTable = ({ +export function CustomTable({ isLoading, - disableKey, + setSort, ...props -}: Props): JSX.Element => ( - { + const field = order === "asc" ? colKey : `-${colKey}`; + if (setSort) { + setSort(field); } - $colWidths={ - props?.cols ? props.cols.map((col) => get(col, "width", "auto")) : [] + }; + + return ( + get(col, "width", "auto")) : [] + } + /> + ); +} + +// React elements can't be serialized into json +function replacer(_key: string, value: unknown) { + if (typeof value === "object" && value != null) { + if ( + "$$typeof" in value && + value.$$typeof != null && + // eslint-disable-next-line @typescript-eslint/no-base-to-string + value.$$typeof.toString() === "Symbol(react.element)" + ) { + return undefined; } - {...props} - /> -); + } + return value; +} const A = styled(Link)` color: black; diff --git a/apps/admin-ui/src/spa/application-rounds/[id]/review/AllocatedEventsTable.tsx b/apps/admin-ui/src/spa/application-rounds/[id]/review/AllocatedEventsTable.tsx index 110f24cd9..efbb1ad41 100644 --- a/apps/admin-ui/src/spa/application-rounds/[id]/review/AllocatedEventsTable.tsx +++ b/apps/admin-ui/src/spa/application-rounds/[id]/review/AllocatedEventsTable.tsx @@ -166,7 +166,6 @@ export function AllocatedEventsTable({ const sortDirection = sort?.startsWith("-") ? "desc" : "asc"; return (