Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .reports/embedded-react-sdk.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5519,6 +5519,7 @@ export interface SwitchProps extends SharedHorizontalFieldLayoutProps, Pick<Inpu
export interface TableData {
content: ReactNode;
key: string;
wrap?: boolean;
}

// @public
Expand Down
9 changes: 5 additions & 4 deletions docs/reference/component-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -1324,10 +1324,11 @@ Shape of a single cell your [Table](#tableprops) implementation receives for hea

##### Properties

| Property | Type | Description |
| ------ | ------ | ------ |
| `content` | `ReactNode` | Content to be displayed in the table cell |
| `key` | `string` | Unique identifier for the table cell |
| Property | Type | Default value | Description |
| ------ | ------ | ------ | ------ |
| `content` | `ReactNode` | | Content to be displayed in the table cell |
| `key` | `string` | | Unique identifier for the table cell |
| `wrap?` | `boolean` | `false` | When true, the cell allows its content to wrap onto multiple lines within a bounded width instead of staying on a single line. Use for columns holding long free-form text (descriptions, notes, messages); otherwise cells hug their content and the table scrolls horizontally. |

<a id="tablerow"></a>

Expand Down
2 changes: 2 additions & 0 deletions src/components/Common/DataView/DataTable/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export const DataTable = <T,>({
...columns.map((column, index) => ({
key: typeof column.key === 'string' ? column.key : `header-${index}`,
content: withJustify(column.title, column.justify),
wrap: column.wrap,
})),
...(itemMenu
? [
Expand Down Expand Up @@ -167,6 +168,7 @@ export const DataTable = <T,>({
return {
key: typeof column.key === 'string' ? column.key : `cell-${colIndex}`,
content: withJustify(getCellContent(item, column), column.justify),
wrap: column.wrap,
}
}),
...(itemMenu
Expand Down
2 changes: 2 additions & 0 deletions src/components/Common/DataView/useDataView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ type DataViewColumn<T> =
title: string | React.ReactNode
render?: (item: T) => React.ReactNode
justify?: 'start' | 'end'
wrap?: boolean
}
| {
key?: string
title: string | React.ReactNode
render: (item: T) => React.ReactNode
justify?: 'start' | 'end'
wrap?: boolean
}

type FooterKeys<T> = keyof T | string
Expand Down
1 change: 1 addition & 0 deletions src/components/Common/DocumentList/DocumentList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ function DocumentList({
columns: [
{
title: columnLabels.form,
wrap: true,
render: (form: FormData) => (
<>
<Components.Text weight="medium" size="sm">
Expand Down
66 changes: 19 additions & 47 deletions src/components/Common/UI/Table/Table.module.scss
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
.root {
width: 100%;
overflow-x: auto;
background: var(--g-colorBody);
border: 1px solid var(--g-colorBorderSecondary);
border-radius: toRem(12);
box-shadow: var(--g-shadowResting);

:global(.react-aria-Table) {
background: var(--g-colorBody);
border: 1px solid var(--g-colorBorderSecondary);
outline: none;
border-spacing: 0;
min-height: 100px;
align-self: start;
width: 100%;
word-break: break-word;
forced-color-adjust: none;
border-radius: toRem(12);
border-collapse: separate;
box-shadow: var(--g-shadowResting);

&[data-focus-visible] {
outline: var(--g-focusRingWidth) solid var(--g-focusRingColor);
Expand All @@ -27,14 +29,6 @@
background-color: var(--g-colorBodyAccent);
color: var(--g-colorBodySubContent);
border-bottom: 1px solid var(--g-colorBorderSecondary);

&:first-child {
border-top-left-radius: toRem(12);
}

&:last-child {
border-top-right-radius: toRem(12);
}
}

//Produces spacing between header and body
Expand Down Expand Up @@ -101,6 +95,12 @@
text-align: left;
outline: none;
word-break: normal;
white-space: nowrap;

&[data-wrap] {
white-space: normal;
max-width: toRem(320);
}

&[data-focus-visible] {
outline: var(--g-focusRingWidth) solid var(--g-focusRingColor);
Expand Down Expand Up @@ -129,12 +129,12 @@
}

:global(.react-aria-Column) {
padding-left: toRem(16);
padding-right: toRem(16);
padding-left: toRem(40);
padding-right: toRem(40);
height: toRem(40);
}
:global(.react-aria-Cell) {
padding: toRem(14) toRem(16);
padding: toRem(14) toRem(40);
transform: translateZ(0);
border-bottom: 1px solid var(--g-colorBorderSecondary);
vertical-align: middle;
Expand All @@ -151,14 +151,6 @@

:global(.react-aria-Row[data-footer='true'] .react-aria-Cell) {
background-color: var(--g-colorBodyAccent);

&:first-child {
border-bottom-left-radius: toRem(12);
}

&:last-child {
border-bottom-right-radius: toRem(12);
}
}

/* join selected items if :has selector is supported */
Expand All @@ -176,33 +168,13 @@
}

&[data-within-box] {
border: none;
border-radius: 0;
box-shadow: none;
background: transparent;

:global(.react-aria-Table) {
border: none;
border-radius: 0;
box-shadow: none;
background: transparent;

:global(.react-aria-TableHeader) {
th {
&:first-child {
border-top-left-radius: 0;
}

&:last-child {
border-top-right-radius: 0;
}
}
}

:global(.react-aria-Row[data-footer='true'] .react-aria-Cell) {
&:first-child {
border-bottom-left-radius: 0;
}

&:last-child {
border-bottom-right-radius: 0;
}
}
}
}
}
75 changes: 75 additions & 0 deletions src/components/Common/UI/Table/Table.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,78 @@ export const WithinBox = () => {

return <Table aria-label="Table within a box" headers={headers} rows={rows} isWithinBox />
}

export const HorizontalScroll = () => {
const { Table } = useComponentContext()

const columnKeys = [
'ID',
'First name',
'Last name',
'Email',
'Department',
'Job title',
'Location',
'Start date',
'Manager',
'Status',
]

const headers: TableData[] = columnKeys.map(key => ({
key: `${key}-header`,
content: key,
}))

const people = [
[
'1',
'John',
'Doe',
'john@example.com',
'Engineering',
'Staff Engineer',
'San Francisco, CA',
'2021-03-15',
'Jane Smith',
'Active',
],
[
'2',
'Jane',
'Smith',
'jane@example.com',
'Engineering',
'Engineering Manager',
'New York, NY',
'2019-07-01',
'Bob Johnson',
'Active',
],
[
'3',
'Bob',
'Johnson',
'bob@example.com',
'Design',
'Principal Designer',
'Remote',
'2020-11-20',
'Jane Smith',
'On leave',
],
]

const rows: TableRow[] = people.map((person, rowIndex) => ({
key: `row-${rowIndex}`,
data: person.map((value, colIndex) => ({
key: `cell-${rowIndex}-${colIndex}`,
content: value,
})),
}))

return (
<div style={{ maxWidth: '600px' }}>
<Table aria-label="Employees" headers={headers} rows={rows} />
</div>
)
}
40 changes: 34 additions & 6 deletions src/components/Common/UI/Table/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,43 @@ import { applyMissingDefaults } from '@/helpers/applyMissingDefaults'
*/
export function Table(rawProps: TableProps) {
const resolvedProps = applyMissingDefaults(rawProps, TableDefaults)
const { className, headers, rows, footer, emptyState, isWithinBox, hasCheckboxColumn, ...props } =
resolvedProps
const {
className,
headers,
rows,
footer,
emptyState,
isWithinBox,
hasCheckboxColumn,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledby,
...props
} = resolvedProps
return (
<div className={styles.root} data-within-box={isWithinBox || undefined}>
<AriaTable {...props} className={classnames('react-aria-Table', className)}>
<div
className={styles.root}
data-within-box={isWithinBox || undefined}
role="region"
aria-label={ariaLabel}
aria-labelledby={ariaLabelledby}
// Keyboard users must be able to scroll the table horizontally when it overflows (WCAG 2.1.1)
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex={0}
>
<AriaTable
{...props}
aria-label={ariaLabel}
aria-labelledby={ariaLabelledby}
className={classnames('react-aria-Table', className)}
>
<AriaTableHeader>
<Row>
{headers.map((header, index) => (
<Column key={header.key} isRowHeader={index === 0}>
<Column
key={header.key}
isRowHeader={index === 0}
data-wrap={header.wrap || undefined}
>
<Text as="span" weight="semibold" size="xs">
{header.content}
</Text>
Expand All @@ -47,7 +75,7 @@ export function Table(rawProps: TableProps) {
rows.map(row => (
<Row key={row.key}>
{row.data.map((cell, index) => (
<Cell key={cell.key}>
<Cell key={cell.key} data-wrap={cell.wrap || undefined}>
<Text
as="span"
variant={
Expand Down
9 changes: 9 additions & 0 deletions src/components/Common/UI/Table/TableTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ export interface TableData {
* Content to be displayed in the table cell
*/
content: ReactNode
/**
* When true, the cell allows its content to wrap onto multiple lines within a
* bounded width instead of staying on a single line. Use for columns holding
* long free-form text (descriptions, notes, messages); otherwise cells hug
* their content and the table scrolls horizontally.
*
* @defaultValue `false`
*/
wrap?: boolean
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ function DocumentsCardContent({ contractorId, onEvent, LoaderComponent }: Docume
{
key: 'title',
title: t('nameColumn'),
wrap: true,
render: (document: Document) => (
<Components.Text weight="medium" size="sm">
{document.title ?? document.name ?? t('emptyPlaceholder')}
Expand All @@ -79,6 +80,7 @@ function DocumentsCardContent({ contractorId, onEvent, LoaderComponent }: Docume
{
key: 'description',
title: t('descriptionColumn'),
wrap: true,
render: (document: Document) => (
<Components.Text variant="supporting" size="sm">
{document.description ?? t('emptyPlaceholder')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ function DeductionsCardContent({ employeeId, onEvent, LoaderComponent }: Deducti
{
key: 'description',
title: t('columns.deduction'),
wrap: true,
render: (garnishment: Garnishment) => garnishment.description || '-',
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export function DeductionsList({
{
key: 'description',
title: t('nameColumn'),
wrap: true,
},
{
key: 'recurring',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ function DocumentsCardContent({ employeeId, onEvent, LoaderComponent }: Document
{
key: 'title',
title: t('columns.title'),
wrap: true,
render: (form: Form) => form.title || '-',
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ function Root({ className, companyId, dictionary, onEvent }: PayrollBlockerListP
columns: [
{
title: t('blockerSectionLabel'),
wrap: true,
render: blocker => (
<FlexItem flexGrow={1}>
<Flex flexDirection="column" gap={8}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ export const PayrollEditEmployeePresentation = ({
{
key: 'description',
title: t('reimbursementDescriptionColumn'),
wrap: true,
render: row => row.description.trim() || t('reimbursementUnnamedFallback'),
},
{
Expand Down
Loading
Loading