Skip to content

Commit f92d187

Browse files
authored
Merge pull request #512 from buildo/table-rows
Allow interactive rows on Table
2 parents d4b4c01 + 71689df commit f92d187

File tree

5 files changed

+88
-11
lines changed

5 files changed

+88
-11
lines changed

Diff for: packages/bento-design-system/src/Table/Config.ts

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { TooltipPlacement } from "../Field/FieldProps";
22
import { IconProps } from "../Icons";
33
import { IllustrationProps } from "../Illustrations";
44
import { BentoSprinkles } from "../internal";
5+
import { vars } from "../vars.css";
56

67
type CellPaddingConfig = {
78
paddingX: BentoSprinkles["paddingX"];
@@ -16,6 +17,10 @@ export type TableConfig = {
1617
hintPlacement: TooltipPlacement;
1718
cellTooltipPlacement: TooltipPlacement;
1819
evenRowsBackgroundColor: BentoSprinkles["background"];
20+
// NOTE(gabro): not using BentoSprinkles["background"] because we only want
21+
// "plain" values to use directly in CSS and not conditional objects like
22+
// { default: ..., hover: ... }
23+
selectedRowBackgroundColor: keyof typeof vars.backgroundColor;
1924
padding: {
2025
header: CellPaddingConfig;
2126
defaultCell: CellPaddingConfig;

Diff for: packages/bento-design-system/src/Table/Table.css.ts

+30-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { style } from "@vanilla-extract/css";
1+
import { createVar, style } from "@vanilla-extract/css";
22
import { bentoSprinkles } from "../internal";
3+
import { strictRecipe } from "../util/strictRecipe";
34

45
export const table = style({
56
gridAutoRows: "max-content",
@@ -29,11 +30,34 @@ export const stickyColumnHeader = bentoSprinkles({
2930
top: 0,
3031
});
3132

32-
export const cellContainer = bentoSprinkles({
33-
height: "full",
34-
display: "flex",
35-
flexDirection: "column",
36-
justifyContent: "center",
33+
export const rowContainer = style({
34+
// NOTE(gabro): this allows us to use the entire row as a parent selector,
35+
// for applying a hover effect on all of its children or clicking on row,
36+
// without intrucing a DOM container that would break the grid layout.
37+
display: "contents",
38+
});
39+
40+
export const selectedRowBackgroundColor = createVar();
41+
42+
export const cellContainerRecipe = strictRecipe({
43+
base: bentoSprinkles({
44+
height: "full",
45+
display: "flex",
46+
flexDirection: "column",
47+
justifyContent: "center",
48+
}),
49+
variants: {
50+
interactiveRow: {
51+
true: {
52+
selectors: {
53+
[`${rowContainer}:hover &`]: {
54+
cursor: "pointer",
55+
background: selectedRowBackgroundColor,
56+
},
57+
},
58+
},
59+
},
60+
},
3761
});
3862

3963
export const sectionHeaderContainer = style([

Diff for: packages/bento-design-system/src/Table/Table.tsx

+45-5
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@ import {
2626
IconButton,
2727
Tooltip,
2828
FeedbackProps,
29+
vars,
2930
} from "..";
3031
import {
31-
cellContainer,
32+
cellContainerRecipe,
3233
columnHeader,
3334
lastLeftStickyColumn,
35+
rowContainer,
3436
sectionHeader,
3537
sectionHeaderContainer,
38+
selectedRowBackgroundColor,
3639
sortIconContainer,
3740
stickyColumnHeader,
3841
table,
@@ -42,6 +45,7 @@ import { useLayoutEffect, useMemo, useState, CSSProperties, useEffect } from "re
4245
import { IconHelp, IconInfo } from "../Icons";
4346
import { match, __ } from "ts-pattern";
4447
import { useBentoConfig } from "../BentoConfigContext";
48+
import { assignInlineVars } from "@vanilla-extract/dynamic";
4549

4650
type SortFn<C extends ReadonlyArray<ColumnType<string, {}, any>>> = (
4751
a: Row<RowType<C>>,
@@ -80,6 +84,7 @@ type Props<C extends ReadonlyArray<ColumnType<string, {}, any>>> = {
8084
initialSorting?: Array<SortingRule<C>>;
8185
stickyHeaders?: boolean;
8286
height?: { custom: string | number };
87+
onRowPress?: (row: Row<RowType<C>>) => void;
8388
} & SortingProps<C>;
8489

8590
/**
@@ -113,6 +118,7 @@ export function Table<C extends ReadonlyArray<ColumnType<string, {}, any>>>({
113118
initialSorting,
114119
stickyHeaders,
115120
height,
121+
onRowPress,
116122
}: Props<C>) {
117123
const config = useBentoConfig().table;
118124
const customOrderByFn = useMemo(
@@ -273,7 +279,11 @@ export function Table<C extends ReadonlyArray<ColumnType<string, {}, any>>>({
273279
.map(({ gridWidth = "fit-content" }) => gridWidthStyle(gridWidth))
274280
.join(" ");
275281

276-
function renderCells<D extends Record<string, unknown>>(cells: Array<Cell<D>>, rowIndex: number) {
282+
function renderCells<D extends Record<string, unknown>>(
283+
cells: Array<Cell<D>>,
284+
rowIndex: number,
285+
interactiveRow: boolean
286+
) {
277287
return cells.map((cell, index) => (
278288
<CellContainer
279289
{...cell.getCellProps()}
@@ -282,6 +292,7 @@ export function Table<C extends ReadonlyArray<ColumnType<string, {}, any>>>({
282292
style={stickyLeftColumnStyle[cell.column.id]}
283293
first={index === 0}
284294
last={(index + 1) % columns.length === 0}
295+
interactiveRow={interactiveRow}
285296
>
286297
{cell.render("Cell")}
287298
</CellContainer>
@@ -321,18 +332,45 @@ export function Table<C extends ReadonlyArray<ColumnType<string, {}, any>>>({
321332
/>,
322333
...row.leafRows.map((row, index) => {
323334
prepareRow(row);
324-
return renderCells(row.cells, index);
335+
return renderCells(row.cells, index, false);
325336
}),
326337
];
327338
} else {
328339
prepareRow(row);
329-
return [renderCells(row.cells, index)];
340+
return (
341+
<RowContainer key={index} row={row} onPress={onRowPress}>
342+
{renderCells(row.cells, index, onRowPress !== undefined)}
343+
</RowContainer>
344+
);
330345
}
331346
})}
332347
</Box>
333348
);
334349
}
335350

351+
function RowContainer<C extends ReadonlyArray<ColumnType<string, {}, any>>>({
352+
row,
353+
children,
354+
onPress,
355+
}: {
356+
row: Row<RowType<C>>;
357+
onPress: ((row: Row<RowType<C>>) => void) | undefined;
358+
children: Children;
359+
}) {
360+
const config = useBentoConfig().table;
361+
return (
362+
<Box
363+
className={rowContainer}
364+
style={assignInlineVars({
365+
[selectedRowBackgroundColor]: vars.backgroundColor[config.selectedRowBackgroundColor],
366+
})}
367+
onClick={() => onPress?.(row)}
368+
>
369+
{children}
370+
</Box>
371+
);
372+
}
373+
336374
function ColumnHeader<D extends Record<string, unknown>>({
337375
column,
338376
style,
@@ -484,6 +522,7 @@ function CellContainer({
484522
lastLeftSticky,
485523
first,
486524
last,
525+
interactiveRow,
487526
...props
488527
}: {
489528
children: any;
@@ -492,14 +531,15 @@ function CellContainer({
492531
lastLeftSticky: boolean;
493532
first: boolean;
494533
last: boolean;
534+
interactiveRow: boolean;
495535
} & TableCellProps) {
496536
const tableConfig = useBentoConfig().table;
497537

498538
return (
499539
<Box className={lastLeftSticky && lastLeftStickyColumn} style={style}>
500540
<Box
501541
background={index % 2 === 0 ? tableConfig.evenRowsBackgroundColor : "backgroundPrimary"}
502-
className={cellContainer}
542+
className={cellContainerRecipe({ interactiveRow })}
503543
paddingLeft={first ? tableConfig.boundaryPadding : undefined}
504544
paddingRight={last ? tableConfig.boundaryPadding : undefined}
505545
{...props}

Diff for: packages/bento-design-system/src/util/defaultConfigs.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ export const table: TableConfig = {
467467
hintPlacement: "top",
468468
cellTooltipPlacement: "bottom",
469469
evenRowsBackgroundColor: "backgroundSecondary",
470+
selectedRowBackgroundColor: "backgroundInteractiveOverlay",
470471
padding: {
471472
header: { paddingX: 16, paddingY: 8 },
472473
defaultCell: { paddingX: 16, paddingY: 16 },

Diff for: packages/storybook/stories/Components/Table.stories.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -438,3 +438,10 @@ export const CustomizedColumns = createStory({
438438
columns: customizedColumns,
439439
data: exampleData,
440440
});
441+
442+
export const InteractiveRow = createStory({
443+
columns: exampleColumns,
444+
data: exampleData,
445+
initialSorting: [{ id: "name" }],
446+
onRowPress: action("onRowPress"),
447+
});

0 commit comments

Comments
 (0)