Skip to content

Commit a3fd707

Browse files
feat(design-system): dsTable "expandable" via extra column [AR-49835] (#454)
[Jira](https://drivenets.atlassian.net/browse/AR-49835)
1 parent 8723a60 commit a3fd707

12 files changed

Lines changed: 87 additions & 88 deletions

File tree

packages/design-system/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,6 @@
3838
width: vars.$reorder-column-width;
3939
}
4040

41-
.expandColumn {
42-
width: vars.$expand-column-width;
43-
padding: 0;
44-
}
45-
4641
.headerCell {
4742
@include typography.body-sm-md;
4843
color: var(--font-main);

packages/design-system/src/components/ds-table/components/ds-table-header/ds-table-header.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { useDsTableContext } from '../../context/ds-table-context';
1010
import { getColumnSizeStyle } from '../../utils/column-size';
1111

1212
const DsTableHeader = <TData,>({ table }: DsTableHeaderProps<TData>) => {
13-
const { stickyHeader, bordered, expandable, selectable, reorderable, showSelectAllCheckbox, virtualized } =
13+
const { stickyHeader, bordered, selectable, reorderable, showSelectAllCheckbox, virtualized } =
1414
useDsTableContext<TData, unknown>();
1515

1616
return (
@@ -47,24 +47,24 @@ const DsTableHeader = <TData,>({ table }: DsTableHeaderProps<TData>) => {
4747
)}
4848
</TableHead>
4949
)}
50-
{expandable && <TableHead className={classnames(styles.headerCell, styles.expandColumn)} />}
5150
{reorderable && (
5251
<TableHead className={classnames(styles.headerCell, styles.reorderColumn)}>Order</TableHead>
5352
)}
5453
{headerGroup.headers.map((header) => {
5554
const headerStyle = getColumnSizeStyle(header.column.getSize(), virtualized);
55+
const canSort = header.column.getCanSort();
5656

5757
return (
5858
<TableHead
5959
key={header.id}
60-
className={classnames(styles.headerCell, header.column.getCanSort() && styles.sortableHeader)}
60+
className={classnames(styles.headerCell, canSort && styles.sortableHeader)}
6161
onClick={header.column.getToggleSortingHandler()}
6262
style={headerStyle}
6363
>
6464
{header.isPlaceholder ? null : (
6565
<div className={styles.headerSortContainer}>
6666
<div>{flexRender(header.column.columnDef.header, header.getContext())}</div>
67-
{header.column.getCanSort() && (
67+
{canSort && (
6868
<div className={styles.pageButtonIconContainer}>
6969
{{
7070
asc: (
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
@use '../../styles/variables' as vars;
2-
3-
.cell {
4-
width: vars.$expand-column-width;
5-
padding: 0;
6-
}
7-
81
.button {
92
margin: auto;
103
}
Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,36 @@
11
import type React from 'react';
22
import classnames from 'classnames';
3-
import { TableCell } from '../core-table';
43
import { DsButton } from '../../../ds-button';
54
import { DsIcon } from '../../../ds-icon';
65
import stylesShared from '../../styles/shared/ds-table-shared.module.scss';
7-
import { useDsTableContext } from '../../context/ds-table-context';
86
import type { DsTableRowExpandableCellProps } from './ds-table-row-expandable-cell.types';
97
import styles from './ds-table-row-expandable-cell.module.scss';
108

119
export const DsTableRowExpandableCell = <TData,>({
1210
row,
13-
className,
1411
buttonClassName,
1512
}: DsTableRowExpandableCellProps<TData>) => {
16-
const { expandable } = useDsTableContext<TData, unknown>();
1713
const isExpanded = row.getIsExpanded();
18-
const isExpandable = typeof expandable === 'function' ? expandable(row.original) : expandable;
1914

2015
return (
21-
<TableCell className={classnames(styles.cell, className)}>
22-
{isExpandable && (
23-
<DsButton
24-
design="v1.2"
25-
buttonType="tertiary"
26-
size="small"
27-
className={classnames(styles.button, buttonClassName)}
28-
onClick={(e: React.MouseEvent) => {
29-
e.stopPropagation();
30-
row.toggleExpanded();
31-
}}
32-
onDoubleClick={(e: React.MouseEvent) => {
33-
e.stopPropagation();
34-
}}
35-
>
36-
<DsIcon
37-
icon="chevron_right"
38-
size="small"
39-
className={classnames(stylesShared.pageButtonIcon, isExpanded && 'rotate-90')}
40-
/>
41-
</DsButton>
42-
)}
43-
</TableCell>
16+
<DsButton
17+
design="v1.2"
18+
buttonType="tertiary"
19+
size="small"
20+
className={classnames(styles.button, buttonClassName)}
21+
onClick={(e: React.MouseEvent) => {
22+
e.stopPropagation();
23+
row.toggleExpanded();
24+
}}
25+
onDoubleClick={(e: React.MouseEvent) => {
26+
e.stopPropagation();
27+
}}
28+
>
29+
<DsIcon
30+
icon="chevron_right"
31+
size="small"
32+
className={classnames(stylesShared.pageButtonIcon, isExpanded && 'rotate-90')}
33+
/>
34+
</DsButton>
4435
);
4536
};

packages/design-system/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.types.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ export interface DsTableRowExpandableCellProps<TData> {
66
* expanded state when the button is clicked.
77
*/
88
row: Row<TData>;
9-
/**
10-
* Additional CSS class name applied to the cell element.
11-
*/
12-
className?: string;
139
/**
1410
* Additional CSS class name applied to the expand/collapse button.
1511
*/

packages/design-system/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@
5353
}
5454

5555
.cell,
56-
.selectableCell,
57-
.expandableCell {
56+
.selectableCell {
5857
display: flex;
5958
align-items: center;
6059
@include typography.body-sm-reg;
@@ -65,6 +64,11 @@
6564
padding: 0 var(--standard);
6665
}
6766

67+
.expandableCell {
68+
padding: 0;
69+
justify-content: center;
70+
}
71+
6872
.expandedRowContent {
6973
background-color: var(--background-secondary);
7074

packages/design-system/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { useDsTableContext } from '../../context/ds-table-context';
44
import { getColumnSizeStyle } from '../../utils/column-size';
55
import { TableCell, TableRow } from '../core-table';
66
import { DsTableRowSelectableCell } from '../ds-table-row-selectable-cell';
7-
import { DsTableRowExpandableCell } from '../ds-table-row-expandable-cell';
87
import type { DsTableRowVirtualizedProps } from './ds-table-row-virtualized.types';
98
import { DsTableCell } from '../ds-table-cell';
9+
import { EXPANDER_COLUMN_ID } from '../../utils/constants';
1010

1111
export const DsTableRowVirtualized = <TData,>({
1212
row,
@@ -19,7 +19,6 @@ export const DsTableRowVirtualized = <TData,>({
1919
}: DsTableRowVirtualizedProps<TData>) => {
2020
const {
2121
selectable,
22-
expandable,
2322
bordered,
2423
rowSize,
2524
activeRowId,
@@ -71,13 +70,19 @@ export const DsTableRowVirtualized = <TData,>({
7170
{selectable && (
7271
<DsTableRowSelectableCell row={row} isSelected={isSelected} className={styles.selectableCell} />
7372
)}
74-
{expandable && <DsTableRowExpandableCell row={row} className={styles.expandableCell} />}
7573
{row.getVisibleCells().map((cell, idx) => {
7674
const isLastColumn = idx === row.getVisibleCells().length - 1;
7775
const cellStyle = getColumnSizeStyle(cell.column.getSize(), true);
7876

7977
return (
80-
<TableCell key={cell.id} style={cellStyle} className={styles.cell}>
78+
<TableCell
79+
key={cell.id}
80+
style={cellStyle}
81+
className={classnames(
82+
styles.cell,
83+
cell.column.id === EXPANDER_COLUMN_ID && styles.expandableCell,
84+
)}
85+
>
8186
{isLastColumn ? (
8287
<DsTableCell
8388
row={row}

packages/design-system/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,7 @@
7272
}
7373

7474
.tableCell,
75-
.selectableCell,
76-
.expandableCell {
75+
.selectableCell {
7776
@include typography.body-sm-reg;
7877
vertical-align: middle;
7978
color: var(--font-main);
@@ -83,15 +82,7 @@
8382
padding: 0 var(--standard);
8483
}
8584

86-
.expandToggleButton {
87-
background: none;
88-
border: none;
85+
.expandableCell {
8986
padding: 0;
90-
margin: 0;
91-
cursor: pointer;
92-
display: inline-flex;
93-
align-items: center;
94-
justify-content: center;
95-
opacity: 1;
96-
transition-property: opacity, background-color;
87+
text-align: center;
9788
}

packages/design-system/src/components/ds-table/components/ds-table-row/ds-table-row.tsx

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import { DsIcon } from '../../../ds-icon';
66
import { TableCell, TableRow } from '../core-table';
77
import { DsTableCell } from '../ds-table-cell';
88
import { DsTableRowSelectableCell } from '../ds-table-row-selectable-cell';
9-
import { DsTableRowExpandableCell } from '../ds-table-row-expandable-cell';
109
import type { DsTableRowProps } from './ds-table-row.types';
1110
import styles from './ds-table-row.module.scss';
1211
import { useDsTableContext } from '../../context/ds-table-context';
1312
import { mergeRefs } from '../../../../utils/merge-refs';
1413
import { getColumnSizeStyle } from '../../utils/column-size';
14+
import { EXPANDER_COLUMN_ID } from '../../utils/constants';
1515

1616
interface DsRowDragHandleProps {
1717
isDragging: boolean;
@@ -40,7 +40,6 @@ const DsRowDragHandle = ({ isDragging, attributes, listeners }: DsRowDragHandleP
4040

4141
const DsTableRow = <TData,>({ ref, row, isSelected }: DsTableRowProps<TData>) => {
4242
const {
43-
expandable,
4443
selectable,
4544
reorderable,
4645
onRowClick,
@@ -53,7 +52,6 @@ const DsTableRow = <TData,>({ ref, row, isSelected }: DsTableRowProps<TData>) =>
5352
activeRowId,
5453
} = useDsTableContext<TData, unknown>();
5554
const isExpanded = row.getIsExpanded();
56-
const isExpandable = typeof expandable === 'function' ? expandable(row.original) : expandable;
5755
const isActive = activeRowId === row.id;
5856

5957
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
@@ -99,13 +97,6 @@ const DsTableRow = <TData,>({ ref, row, isSelected }: DsTableRowProps<TData>) =>
9997
{selectable && (
10098
<DsTableRowSelectableCell row={row} isSelected={isSelected} className={styles.selectableCell} />
10199
)}
102-
{expandable && (
103-
<DsTableRowExpandableCell
104-
row={row}
105-
className={styles.expandableCell}
106-
buttonClassName={styles.expandToggleButton}
107-
/>
108-
)}
109100
{reorderable && (
110101
<DsRowDragHandle isDragging={isDragging} attributes={attributes} listeners={listeners} />
111102
)}
@@ -114,7 +105,14 @@ const DsTableRow = <TData,>({ ref, row, isSelected }: DsTableRowProps<TData>) =>
114105
const cellStyle = getColumnSizeStyle(cell.column.getSize());
115106

116107
return (
117-
<TableCell key={cell.id} className={styles.tableCell} style={cellStyle}>
108+
<TableCell
109+
key={cell.id}
110+
className={classnames(
111+
styles.tableCell,
112+
cell.column.id === EXPANDER_COLUMN_ID && styles.expandableCell,
113+
)}
114+
style={cellStyle}
115+
>
118116
{isLastColumn ? (
119117
<DsTableCell
120118
row={row}
@@ -133,12 +131,7 @@ const DsTableRow = <TData,>({ ref, row, isSelected }: DsTableRowProps<TData>) =>
133131
{isExpanded && renderExpandedRow && (
134132
<TableRow className={styles.expandedRow}>
135133
<TableCell
136-
colSpan={
137-
row.getVisibleCells().length +
138-
(selectable ? 1 : 0) +
139-
(isExpandable ? 1 : 0) +
140-
(reorderable ? 1 : 0)
141-
}
134+
colSpan={row.getVisibleCells().length + (selectable ? 1 : 0) + (reorderable ? 1 : 0)}
142135
className={styles.tableCell}
143136
>
144137
{renderExpandedRow(row.original)}

packages/design-system/src/components/ds-table/ds-table.tsx

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import * as React from 'react';
2-
import { useImperativeHandle } from 'react';
2+
import { useImperativeHandle, useMemo } from 'react';
33
import {
4+
type ColumnDef,
45
type RowSelectionState,
56
type ColumnFiltersState,
67
getCoreRowModel,
8+
getExpandedRowModel,
79
getFilteredRowModel,
810
getSortedRowModel,
911
type SortingState,
@@ -17,10 +19,11 @@ import { DsTableHeader } from './components/ds-table-header';
1719
import styles from './ds-table.module.scss';
1820
import type { DsDataTableProps, DsTableRowSize } from './ds-table.types';
1921
import { DsTableRow } from './components/ds-table-row';
22+
import { DsTableRowExpandableCell } from './components/ds-table-row-expandable-cell';
2023
import { useDragAndDrop } from './hooks/use-drag-and-drop';
2124
import { type DsTableContextType, DsTableContext } from './context/ds-table-context';
2225
import { DsTableBodyVirtualized } from './components/ds-table-body-virtualized';
23-
import { EMPTY_TABLE_STATE_TEXT } from './utils/constants';
26+
import { EMPTY_TABLE_STATE_TEXT, EXPANDER_COLUMN_ID, EXPANDER_COLUMN_WIDTH } from './utils/constants';
2427

2528
// Row size to pixel height mapping (matches CSS variables)
2629
const ROW_SIZE_HEIGHT_MAP: Record<DsTableRowSize, number> = {
@@ -34,7 +37,7 @@ const ROW_SIZE_HEIGHT_MAP: Record<DsTableRowSize, number> = {
3437
*/
3538
const DsTable = <TData extends { id: string }, TValue>({
3639
ref,
37-
columns,
40+
columns: columnsProp,
3841
data: tableData,
3942
virtualized = false,
4043
virtualizedOptions,
@@ -122,6 +125,23 @@ const DsTable = <TData extends { id: string }, TValue>({
122125
onSelectionChange?.(newRowSelection);
123126
};
124127

128+
const columns = useMemo<ColumnDef<TData, TValue>[]>(() => {
129+
if (!expandable) {
130+
return columnsProp;
131+
}
132+
133+
const expanderColumn: ColumnDef<TData, TValue> = {
134+
id: EXPANDER_COLUMN_ID,
135+
size: EXPANDER_COLUMN_WIDTH,
136+
enableSorting: false,
137+
enableResizing: false,
138+
header: () => null,
139+
cell: ({ row }) => (row.getCanExpand() ? <DsTableRowExpandableCell row={row} /> : null),
140+
};
141+
142+
return [expanderColumn, ...columnsProp];
143+
}, [columnsProp, expandable]);
144+
125145
const table = useReactTable({
126146
data: reorderable ? data : tableData,
127147
columns,
@@ -133,6 +153,8 @@ const DsTable = <TData extends { id: string }, TValue>({
133153
onColumnVisibilityChange: handleColumnVisibilityChange, // TODO: looks like this is not used, since visibility is handled from the outside
134154
onRowSelectionChange: handleRowSelectionChange,
135155
getRowId: (row) => row.id,
156+
getExpandedRowModel: getExpandedRowModel(),
157+
getRowCanExpand: typeof expandable === 'function' ? (row) => expandable(row.original) : () => expandable,
136158
state: {
137159
sorting,
138160
columnFilters,
@@ -191,7 +213,7 @@ const DsTable = <TData extends { id: string }, TValue>({
191213
const renderEmptyState = () => (
192214
<TableRow>
193215
<TableCell
194-
colSpan={columns.length + (expandable ? 1 : 0) + (selectable ? 1 : 0)}
216+
colSpan={columns.length + (selectable ? 1 : 0) + (reorderable ? 1 : 0)}
195217
className={styles.emptyState}
196218
>
197219
{emptyState || EMPTY_TABLE_STATE_TEXT}

0 commit comments

Comments
 (0)