Skip to content

Commit

Permalink
feat(components): table restyle (#953)
Browse files Browse the repository at this point in the history
* Improve Table styling

* Add sorting functionality

* Make table layout fixed

* Pass all props to Table component

---------

Co-authored-by: sushitommy <>
  • Loading branch information
sushitommy authored Dec 16, 2024
1 parent bdcc538 commit 6c75ec9
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 29 deletions.
8 changes: 8 additions & 0 deletions .changeset/gentle-buses-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@nl-rvo/design-tokens": minor
"@nl-rvo/css-table": minor
"@nl-rvo/component-library-css": minor
"@nl-rvo/component-library-react": minor
---

Improved Table styling
6 changes: 3 additions & 3 deletions components/table/src/defaultArgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { ITableProps } from './template';
export const defaultArgs: ITableProps = {
description: 'Table description.',
columns: [
{ label: 'Title' },
{ label: 'Title', sortable: true },
{ label: 'Text', sortable: true, sortDirection: 'ASC' },
{ label: 'Price ($)', type: 'numeric' },
{ label: 'Link' },
{ label: 'Price ($)', sortable: true, type: 'numeric' },
{ label: 'Link', sortable: true },
],
rows: [
['Title value 1', 'Text value 1', '57', '<a href="#" class="rvo-link">Link 1</a>'],
Expand Down
47 changes: 47 additions & 0 deletions components/table/src/icons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { SVGProps } from 'react';

export const SortDescendingIcon = (props: SVGProps<SVGSVGElement>) => (
<svg width="39" height="48" viewBox="0 0 39 48" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path
d="M9.24057 12.6281C8.97942 12.2439 8.57505 12.0129 8.14198 12.0005C7.70909 11.9881 7.29461 12.1957 7.0161 12.5643L0.745638 20.8504C0.297743 21.4411 0.298239 22.3052 0.746692 22.8959C1.19502 23.4848 1.95962 23.6318 2.55799 23.238L5.76102 21.1364L5.77596 46.4092C5.77596 47.7274 6.7353 48 7.91939 48C9.10349 48 10.0629 47.7274 10.0629 46.4092L10.0581 21.2311L13.2 23.5426C13.7804 23.969 14.5498 23.866 15.0248 23.3046C15.4998 22.7403 15.5402 21.8776 15.1201 21.2619L9.24057 12.6281Z"
fill="var(--rvo-color-grijs-800)"
/>
<path
d="M26.0473 47.1625C26.4129 47.6748 26.979 47.9828 27.5853 47.9993C28.1914 48.0159 28.7716 47.739 29.1615 47.2476L37.9402 36.1995C38.5672 35.4118 38.5665 34.2597 37.9387 33.4721C37.3111 32.687 36.2406 32.4909 35.4029 33.016L30.9187 35.8181L30.8977 2.12105C30.8977 0.363522 29.5547 0 27.8969 0C26.2392 0 24.896 0.363522 24.896 2.12105L24.9028 35.6919L20.5041 32.6099C19.6915 32.0413 18.6144 32.1787 17.9494 32.9272C17.2843 33.6795 17.2278 34.8299 17.8159 35.6508L26.0473 47.1625Z"
fill="var(--rvo-color-hemelblauw)"
/>
</svg>
);

export const SortAscendingIcon = (props: SVGProps<SVGSVGElement>) => (
<svg width="39" height="48" viewBox="0 0 39 48" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path
d="M12.7728 0.837455C12.4072 0.325207 11.8411 0.0171769 11.2348 0.000703278C10.6287 -0.0158624 10.0484 0.260969 9.65853 0.752418L0.879879 11.8005C0.252827 12.5881 0.253521 13.7403 0.881355 14.5279C1.50901 15.313 2.57945 15.5091 3.41717 14.984L7.90141 12.1819L7.92233 45.8789C7.92233 47.6365 9.26541 48 10.9231 48C12.5809 48 13.924 47.6365 13.924 45.8789L13.9173 12.3081L18.316 15.3901C19.1286 15.9587 20.2057 15.8213 20.8707 15.0728C21.5357 14.3205 21.5922 13.1701 21.0042 12.3492L12.7728 0.837455Z"
fill="var(--rvo-color-hemelblauw)"
/>
<path
d="M29.5795 47.3719C29.8406 47.7561 30.245 47.9871 30.6781 47.9995C31.111 48.0119 31.5255 47.8043 31.804 47.4357L38.0744 39.1496C38.5223 38.5589 38.5218 37.6948 38.0734 37.1041C37.625 36.5152 36.8605 36.3682 36.2621 36.762L33.0591 38.8636L33.0441 13.5908C33.0441 12.2726 32.0848 12 30.9007 12C29.7166 12 28.7572 12.2726 28.7572 13.5908L28.762 38.7689L25.6201 36.4574C25.0397 36.031 24.2703 36.134 23.7953 36.6954C23.3203 37.2597 23.2799 38.1224 23.6999 38.7381L29.5795 47.3719Z"
fill="var(--rvo-color-grijs-800)"
/>
</svg>
);

export const SortDefaultIcon = (props: SVGProps<SVGSVGElement>) => (
<svg width="33" height="38" viewBox="0 0 33 38" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<g clipPath="url(#clip0_5888_77)">
<path
d="M23.5795 36.3719C23.8406 36.7561 24.245 36.9871 24.6781 36.9995C25.111 37.0119 25.5255 36.8043 25.804 36.4357L32.0744 28.1496C32.5223 27.5589 32.5218 26.6948 32.0734 26.1041C31.625 25.5152 30.8605 25.3682 30.2621 25.762L27.0591 27.8636L27.0441 2.59079C27.0441 1.27264 26.0848 1 24.9007 1C23.7166 1 22.7572 1.27264 22.7572 2.59079L22.762 27.7689L19.6201 25.4574C19.0397 25.031 18.2703 25.134 17.7953 25.6954C17.3203 26.2597 17.2799 27.1224 17.6999 27.7381L23.5795 36.3719Z"
fill="var(--rvo-color-grijs-800)"
/>
<path
d="M9.24057 1.62809C8.97942 1.24391 8.57505 1.01288 8.14198 1.00053C7.70909 0.988103 7.29461 1.19573 7.0161 1.56431L0.745638 9.85041C0.297743 10.4411 0.298239 11.3052 0.746692 11.8959C1.19502 12.4848 1.95962 12.6318 2.55799 12.238L5.76102 10.1364L5.77596 35.4092C5.77596 36.7274 6.7353 37 7.91939 37C9.10349 37 10.0629 36.7274 10.0629 35.4092L10.0581 10.2311L13.2 12.5426C13.7804 12.969 14.5498 12.866 15.0248 12.3046C15.4998 11.7403 15.5402 10.8776 15.1201 10.2619L9.24057 1.62809Z"
fill="var(--rvo-color-grijs-800)"
/>
</g>
<defs>
<clipPath id="clip0_5888_77">
<rect width="32" height="38" fill="white" transform="matrix(-1 0 0 -1 32.41 38)" />
</clipPath>
</defs>
</svg>
);
24 changes: 24 additions & 0 deletions components/table/src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
.rvo-table {
border-collapse: collapse;
border-spacing: 0;
table-layout: fixed;
width: 100%;
}

Expand All @@ -21,6 +22,9 @@

.rvo-table-header {
background-color: var(--rvo-table-header-background-color);
border-bottom-color: var(--rvo-table-header-border-bottom-color);
border-bottom-style: var(--rvo-table-header-border-bottom-style);
border-bottom-width: var(--rvo-table-header-border-bottom-width);
color: var(--rvo-table-header-color);
font-weight: var(--rvo-table-header-font-weight);
padding-block-end: var(--rvo-table-header-padding-block-end);
Expand All @@ -30,6 +34,26 @@
text-align: start;
}

.rvo-table-header__sortable-container {
align-items: center;
display: flex;
gap: var(--rvo-space-sm);
}

.rvo-table-header__sortable-button {
min-inline-size: 0;
padding-block-end: 0;
padding-block-start: 0;
padding-inline-end: 0;
padding-inline-start: 0;
}

.rvo--table-header__sorting-icon {
display: flex;
height: var(--rvo-icon-md-height);
width: var(--rvo-icon-md-width);
}

.rvo-table-header--sortable:hover {
background-color: var(--rvo-table-header-sortable-hover-background-color);
cursor: pointer;
Expand Down
93 changes: 69 additions & 24 deletions components/table/src/template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,25 @@
* Copyright (c) 2021 Community for NL Design System
*/
import clsx from 'clsx';
import React from 'react';
import React, { HTMLAttributes, useCallback, useEffect, useState } from 'react';
import { defaultArgs } from './defaultArgs';
import { Button } from '../../button/src/template';
import { SortAscendingIcon, SortDescendingIcon } from './icons';
import validateHTML from '../../utils/validateHTML';
import './index.scss';

export interface ITableColumnProps {
export interface ITableColumnProps extends HTMLAttributes<HTMLDivElement> {
label: string;
type?: 'numeric';
sortable?: boolean;
sortDirection?: 'ASC' | 'DESC' | '';
key?: string;
}

export interface ITableProps {
description?: string;
columns: ITableColumnProps[];
rows: string[][];
onSort?: (columnIndex: number, direction: 'ASC' | 'DESC' | '') => void;
}

export const argTypes = {
Expand All @@ -40,57 +42,100 @@ export const argTypes = {
},
};

const sortData = (rows: string[][], columnIndex: number, direction: 'ASC' | 'DESC' | '', type?: string): string[][] => {
if (!direction) return rows;

return [...rows].sort((a, b) => {
const aValue = a[columnIndex];
const bValue = b[columnIndex];

if (type === 'numeric') {
const aNum = parseFloat(aValue);
const bNum = parseFloat(bValue);
return direction === 'ASC' ? aNum - bNum : bNum - aNum;
}

return direction === 'ASC' ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
});
};

export const Table: React.FC<ITableProps> = ({
description,
columns = defaultArgs.columns,
rows = defaultArgs.rows,
onSort,
...props
}: ITableProps) => {
const [internalColumns, setInternalColumns] = useState(columns);
const [internalRows, setInternalRows] = useState(rows);

useEffect(() => {
setInternalColumns(columns);
setInternalRows(rows);
}, [columns, rows]);

const handleSort = useCallback(
(columnIndex: number) => {
const newColumns = [...internalColumns];
const currentDirection = newColumns[columnIndex].sortDirection || '';
const newDirection: 'ASC' | 'DESC' | '' =
currentDirection === '' ? 'ASC' : currentDirection === 'ASC' ? 'DESC' : '';

// Reset all other columns
newColumns.forEach((col, i) => {
if (i !== columnIndex) {
col.sortDirection = '';
}
});

newColumns[columnIndex].sortDirection = newDirection;
setInternalColumns(newColumns);
setInternalRows(sortData(rows, columnIndex, newDirection, columns[columnIndex].type));
onSort?.(columnIndex, newDirection);
},
[internalColumns, rows, columns, onSort],
);

return (
<div className="rvo-table--responsive">
<div className="rvo-table--responsive" {...props}>
<table className="rvo-table">
{description && <caption className="rvo-caption">{description}</caption>}
<thead className="rvo-table-head">
<tr className="rvo-table-row">
{columns.map((column, index) => {
let icon: string;
if (column.sortDirection === 'ASC') {
icon = 'delta-omhoog';
} else if (column.sortDirection === 'DESC') {
icon = 'delta-omlaag';
} else {
icon = '';
}

{internalColumns.map((column, index) => {
return (
<th
key={index}
scope="col"
className={clsx(
'rvo-table-header',
column.sortable && 'rvo-table-header--sortable',
column.sortable &&
column.sortDirection &&
column.sortDirection.length > 1 && ['rvo-layout-row', 'rvo-layout-gap--sm'],
column.type === 'numeric' && 'rvo-table-header--numeric',
)}
onClick={column.sortable ? () => handleSort(index) : undefined}
style={column.sortable ? { cursor: 'pointer' } : undefined}
>
{column.label}
{column.sortable && column.sortDirection && column.sortDirection.length > 0 && (
<Button kind="tertiary" showIcon="before" icon={icon as any} label="" size="sm" />
)}
<div className="rvo-table-header__sortable-container">
{column.label}
{column.sortable && column.sortDirection === 'ASC' && (
<SortAscendingIcon className="rvo--table-header__sorting-icon" />
)}
{column.sortable && column.sortDirection === 'DESC' && (
<SortDescendingIcon className="rvo--table-header__sorting-icon" />
)}
</div>
</th>
);
})}
</tr>
</thead>
<tbody className="rvo-table-body">
{rows.map((row, rowIndex) => (
{internalRows.map((row, rowIndex) => (
<tr key={rowIndex} className="rvo-table-row">
{columns.map((column, columnIndex) => {
{internalColumns.map((column, columnIndex) => {
const cellValue = row[columnIndex];
const cellClassNames = clsx('rvo-table-cell', column.type === 'numeric' && 'rvo-table-cell--numeric');

// Parse cell markup (value is either string, HTML string or React node)
let cellMarkup = (
<td key={columnIndex} className={cellClassNames}>
{cellValue}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
},
"table": {
"header": {
"background-color": { "value": "{rvo.color.grijs-050}" },
"background-color": { "value": "{rvo.color.grijs-200}" },
"border-bottom-width": { "value": "{rvo.size.3xs}" },
"border-bottom-style": { "value": "solid" },
"border-bottom-color": { "value": "{rvo.color.grijs-500}" },
"color": { "value": "{rvo.color.grijs-600}" },
"font-weight": { "value": "{rvo.font-weight.normal}" },
"padding-block-end": { "value": "{rvo.space.xs}" },
Expand All @@ -25,7 +28,7 @@
"cell": {
"border-bottom-width": { "value": "0.05rem" },
"border-bottom-style": { "value": "solid" },
"border-bottom-color": { "value": "{rvo.color.grijs-300}" },
"border-bottom-color": { "value": "{rvo.color.grijs-500}" },
"padding-block-end": { "value": "{rvo.space.xs}" },
"padding-block-start": { "value": "{rvo.space.xs}" },
"padding-inline-end": { "value": "{rvo.space.md}" },
Expand Down

0 comments on commit 6c75ec9

Please sign in to comment.