Skip to content

Commit b535d05

Browse files
authored
Table: Add overflowX and whiteSpace props (#603)
* feat(Table): Add `overflowX` and `whiteSpace` props * Add changeset
1 parent 62f2f30 commit b535d05

7 files changed

+84
-20
lines changed

.changeset/forty-melons-rhyme.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'scoobie': minor
3+
---
4+
5+
Table: Add `overflowX` and `whiteSpace` props
6+
7+
These allow you to control overflow and wrapping behaviour when width is constrained.
8+
9+
```tsx
10+
<>
11+
<Table />
12+
13+
<Table overflowX="scroll" />
14+
15+
<Table overflowX="scroll" whiteSpace="nowrap" />
16+
</>
17+
```

src/components/CodeBlock.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export const CodeBlock = ({
7979

8080
return (
8181
<Stack space={tablePadding}>
82-
<ScrollableInline>
82+
<ScrollableInline whiteSpace="nowrap">
8383
<Box display="flex" justifyContent="spaceBetween">
8484
<Box display="flex">
8585
{children.map(({ label }, labelIndex) => (

src/components/Table.stories.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,20 @@ export default {
1111
component: Component,
1212
args: {
1313
size: 'standard',
14-
width: undefined,
1514
},
1615
argTypes: {
16+
overflowX: {
17+
control: { type: 'radio' },
18+
options: [undefined, 'scroll'],
19+
},
1720
size: {
1821
control: { type: 'radio' },
1922
options: ['standard', 'large'],
2023
},
24+
whiteSpace: {
25+
control: { type: 'radio' },
26+
options: [undefined, 'nowrap'],
27+
},
2128
width: {
2229
control: { type: 'radio' },
2330
options: [undefined, 'full'],

src/components/Table.tsx

+16-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { DEFAULT_SIZE, type Size } from '../private/size';
1212

1313
import { TableRow } from './TableRow';
1414

15-
interface Props {
15+
interface BaseProps {
1616
align?: readonly TableAlign[];
1717
children: ReactNode;
1818
header: ComponentProps<typeof Stack>['children'] | readonly string[];
@@ -21,15 +21,29 @@ interface Props {
2121
width?: 'full';
2222
}
2323

24+
type Props = BaseProps &
25+
(
26+
| {
27+
overflowX: 'scroll';
28+
whiteSpace?: 'nowrap';
29+
}
30+
| {
31+
overflowX?: never;
32+
whiteSpace?: never;
33+
}
34+
);
35+
2436
export const Table = ({
2537
align,
2638
children,
2739
header,
40+
overflowX,
2841
size = DEFAULT_SIZE,
2942
type = DEFAULT_TABLE_TYPE,
43+
whiteSpace,
3044
width,
3145
}: Props) => (
32-
<BaseTable width={width}>
46+
<BaseTable overflowX={overflowX} whiteSpace={whiteSpace} width={width}>
3347
<TableContext.Provider
3448
value={{
3549
align,

src/private/ScrollableInline.css.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { style } from '@vanilla-extract/css';
1+
import { style, styleVariants } from '@vanilla-extract/css';
22
import { calc } from '@vanilla-extract/css-utils';
33
import { vars } from 'braid-design-system/css';
44

@@ -12,7 +12,11 @@ export const scrollX = style({
1212
overflowX: 'auto',
1313
overflowY: 'hidden',
1414
scrollbarWidth: 'none',
15-
whiteSpace: 'nowrap',
1615

1716
WebkitOverflowScrolling: 'touch',
1817
});
18+
19+
export const whiteSpace = styleVariants(
20+
{ undefined, nowrap: 'nowrap' } as const,
21+
(value) => ({ whiteSpace: value }),
22+
);

src/private/ScrollableInline.tsx

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1-
import { Box } from 'braid-design-system';
1+
import { Bleed, Box } from 'braid-design-system';
22
import React, { type ReactNode } from 'react';
33

44
import * as styles from './ScrollableInline.css';
55

66
interface Props {
77
children: ReactNode;
8+
whiteSpace?: 'nowrap';
89
}
910

10-
export const ScrollableInline = ({ children }: Props) => (
11-
<Box className={styles.trimPaddingY}>
12-
<Box className={styles.scrollX} paddingY="small" width="full">
11+
export const ScrollableInline = ({ children, whiteSpace }: Props) => (
12+
<Bleed vertical="small">
13+
<Box
14+
className={[styles.scrollX, styles.whiteSpace[whiteSpace ?? 'undefined']]}
15+
paddingY="small"
16+
width="full"
17+
>
1318
{children}
1419
</Box>
15-
</Box>
20+
</Bleed>
1621
);

src/private/Table.tsx

+26-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Box } from 'braid-design-system';
2-
import React, { type ReactNode } from 'react';
2+
import React, { Fragment, type ReactNode } from 'react';
33

4+
import { ScrollableInline } from './ScrollableInline';
45
import {
56
DEFAULT_TABLE_CELL_COMPONENT,
67
DEFAULT_TABLE_TYPE,
@@ -13,17 +14,33 @@ import * as styles from './Table.css';
1314

1415
interface BaseTableProps {
1516
children: ReactNode;
17+
overflowX?: 'scroll';
18+
whiteSpace?: 'nowrap';
1619
width?: 'full';
1720
}
1821

19-
export const BaseTable = ({ children, width }: BaseTableProps) => (
20-
<Box
21-
component="table"
22-
className={{ [styles.table]: true, [styles.fullWidth]: width === 'full' }}
23-
>
24-
{children}
25-
</Box>
26-
);
22+
export const BaseTable = ({
23+
children,
24+
overflowX,
25+
whiteSpace,
26+
width,
27+
}: BaseTableProps) => {
28+
const Wrapper = overflowX === 'scroll' ? ScrollableInline : Fragment;
29+
30+
return (
31+
<Wrapper {...(overflowX === 'scroll' ? { whiteSpace } : {})}>
32+
<Box
33+
component="table"
34+
className={{
35+
[styles.table]: true,
36+
[styles.fullWidth]: width === 'full',
37+
}}
38+
>
39+
{children}
40+
</Box>
41+
</Wrapper>
42+
);
43+
};
2744

2845
interface MdxTableProps {
2946
children: ReactNode;

0 commit comments

Comments
 (0)