diff --git a/packages/braid-design-system/src/lib/components/Table/Table.css.ts b/packages/braid-design-system/src/lib/components/Table/Table.css.ts
index ef35a851eb8..b977e398728 100644
--- a/packages/braid-design-system/src/lib/components/Table/Table.css.ts
+++ b/packages/braid-design-system/src/lib/components/Table/Table.css.ts
@@ -5,15 +5,15 @@ import { responsiveStyle } from '../../css/responsiveStyle';
// TABLE
const borderColor = createVar();
-const borderWidth = createVar();
+const sectionBorderWidth = createVar();
export const table = style([
{
vars: {
// [borderWidth]: '1px',
- [borderWidth]: vars.borderWidth.standard,
+ [sectionBorderWidth]: vars.borderWidth.standard,
},
borderCollapse: 'separate',
- border: `${borderWidth} solid ${borderColor}`,
+ border: `${sectionBorderWidth} solid ${borderColor}`,
fontVariantNumeric: 'tabular-nums',
wordBreak: 'break-word',
},
@@ -51,20 +51,6 @@ globalStyle(
},
);
-// export const noSideBorders = style({
-// borderLeftColor: 'transparent',
-// borderRightColor: 'transparent',
-// });
-
-// TABLE HEADER
-export const tableHeaderRounding = style({});
-// globalStyle(`${tableHeaderRounding} > tr > th:first-of-type`, {
-// borderTopLeftRadius: vars.borderRadius.large,
-// });
-// globalStyle(`${tableHeaderRounding} > tr > th:last-of-type`, {
-// borderTopRightRadius: vars.borderRadius.large,
-// });
-
// TABLE CELLS
export const alignYCenter = style({
verticalAlign: 'middle',
@@ -89,18 +75,22 @@ export const maxWidth = style({
maxWidth: maxWidthVar,
});
-export const borderBottom = style({
- borderBottom: `${borderWidth} solid ${borderColor}`,
+export const headerCellBorder = style({});
+globalStyle(`${table} > thead > tr:last-child > ${headerCellBorder}`, {
+ borderBottom: `${sectionBorderWidth} solid ${borderColor}`,
});
-// No border on bottom of last row (table border is used instead)
-globalStyle(`${table} > tbody > tr:last-of-type > *`, {
- borderBottom: 0,
+
+export const bodyCellBorder = style({});
+// Apply border on bottom of all cells except last row of a table section
+globalStyle(`${table} > * > tr:not(:last-child) > *`, {
+ borderBottom: `1px solid ${borderColor}`,
});
-// export const borderTopFooter = style({});
-// globalStyle(`${table} > tfoot > *:first-of-type > *${borderTopFooter}`, {
-// borderTop: `${vars.borderWidth.standard} solid ${borderColor}`,
-// });
+export const footerCellBorder = style({});
+// Apply heavier section border between footer and body sections
+globalStyle(`${table} > tfoot > tr:first-child > ${footerCellBorder}`, {
+ borderTop: `${sectionBorderWidth} solid ${borderColor}`,
+});
export const showOnTablet = style(
responsiveStyle({ tablet: { display: 'table-cell' } }),
diff --git a/packages/braid-design-system/src/lib/components/Table/Table.docs.tsx b/packages/braid-design-system/src/lib/components/Table/Table.docs.tsx
index f2bf368a9da..6538e4d298d 100644
--- a/packages/braid-design-system/src/lib/components/Table/Table.docs.tsx
+++ b/packages/braid-design-system/src/lib/components/Table/Table.docs.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import type { ComponentDocs } from 'site/types';
import {
Badge,
+ Box,
ButtonIcon,
Card,
HiddenVisually,
@@ -12,6 +13,7 @@ import {
Table,
TableBody,
TableCell,
+ TableHeadCell,
TableHeader,
TableRow,
Text,
@@ -23,19 +25,29 @@ import { Placeholder } from '../private/Placeholder/Placeholder';
const docs: ComponentDocs = {
category: 'Layout',
+ subComponents: [
+ 'TableHeader',
+ 'TableFooter',
+ 'TableBody',
+ 'TableRow',
+ 'TableHeadCell',
+ 'TableCell',
+ ],
Example: () =>
source(
-
- Lorem
-
-
- Ipsum
-
-
- Dolor
-
+
+
+ Lorem
+
+
+ Ipsum
+
+
+ Dolor
+
+
@@ -95,70 +107,152 @@ const docs: ComponentDocs = {
{
label: 'Table structure',
description: (
-
- A Table must include a TableBody{' '}
- with one or more TableRow components, with each
- containing TableCell components that represent each
- column of the tabular data.
-
+ <>
+
+ A Table is made up of three sections:{' '}
+ TableHeader, TableBody (required)
+ and TableFooter, each section containing one or
+ more TableRow components. Each row is made of up of{' '}
+ TableCell or TableHeaderCell{' '}
+ components representing each column of the data.
+
+ >
),
- Example: ({ setDefaultState, getState }) => {
- const { code, value } = source(
- <>
- {setDefaultState('rows', [
- {
- column1: 'Sit',
- column2: 'Amet',
- column3: 'Consectetur',
- },
- {
- column1: 'Adipiscing',
- column2: 'Elit',
- column3: 'Praesent',
- },
- {
- column1: 'Semper',
- column2: 'Interdum',
- column3: 'Viverra',
- },
- ])}
-
-
- {getState('rows').map((row: any) => (
-
-
- {row.column1}
-
-
- {row.column2}
-
-
- {row.column3}
-
-
- ))}
-
-
- >,
- );
+ code: false,
+ playroom: false,
+ Example: () =>
+ source(
+
+
+
+ Table
+
+
+
+
+ TableHeader
+
- return {
- code: code.replaceAll(': any', '').replaceAll(' key={row}', ''),
- value,
- };
- },
+
+
+
+ TableRow
+
+
+
+ TableHeaderCell
+
+
+
+
+
+
+
+
+
+ TableBody (required)
+
+
+
+
+
+ TableRow
+
+
+
+
+ TableCell
+
+
+
+
+
+
+
+
+
+
+ TableFooter
+
+
+
+
+
+ TableRow
+
+
+
+
+ TableCell
+
+
+
+
+
+
+
+
+ ,
+ ),
},
{
label: 'Column headings',
description: (
- A TableHeader component can be placed before the{' '}
- TableBody, providing a header row that defaults all{' '}
- TableCell components inside to be{' '}
-
- table header elements
- {' '}
- with relevant styling.
+ A TableHeader can be provided, containing a{' '}
+ TableRow of column headings. Each heading cell should
+ use the TableHeadCell component, providing the
+ relevant semantics, column labelling and styling.
),
Example: ({ setDefaultState, getState }) => {
@@ -183,15 +277,17 @@ const docs: ComponentDocs = {
])}
-
- Lorem
-
-
- Ipsum
-
-
- Dolor
-
+
+
+ Lorem
+
+
+ Ipsum
+
+
+ Dolor
+
+
{getState('rows').map((row: any) => (
@@ -222,9 +318,9 @@ const docs: ComponentDocs = {
label: 'Row headings',
description: (
- Row-level headings are supported by specifying the{' '}
- header prop on the TableCell{' '}
- component.
+ Row-level headings are supported by providing a{' '}
+ TableHeadCell within a TableRow of
+ the TableBody section.
),
Example: ({ setDefaultState, getState }) => {
@@ -251,9 +347,9 @@ const docs: ComponentDocs = {
{getState('rows').map((row: any) => (
-
+
{row.column1}
-
+
{row.column2}
@@ -318,25 +414,27 @@ const docs: ComponentDocs = {
])}
-
- Time
-
-
- Lorem
-
-
- Ipsum
-
-
- Dolor
-
+
+
+ Time
+
+
+ Lorem
+
+
+ Ipsum
+
+
+ Dolor
+
+
{getState('rows').map((row: any) => (
-
+
{row.column1}
-
+
{row.column2}
@@ -364,9 +462,10 @@ const docs: ComponentDocs = {
description: (
<>
- By default, a TableCell has a{' '}
- width of auto, accomodating the
- longest content within the column.
+ By default, a TableCell (and{' '}
+ TableHeadCell) has a width of{' '}
+ auto, accomodating the longest content within the
+ column.
If the table content is larger than the available space, each column
@@ -416,21 +515,23 @@ const docs: ComponentDocs = {
])}
-
- Status
-
-
- Lorem
-
-
- Ipsum
-
-
- Dolor
-
-
- Actions
-
+
+
+ Status
+
+
+ Lorem
+
+
+ Ipsum
+
+
+ Dolor
+
+
+ Actions
+
+
{getState('rows').map((row: any) => (
@@ -516,21 +617,23 @@ const docs: ComponentDocs = {
])}
-
- Status
-
-
- Lorem
-
-
- Ipsum
-
-
- Dolor
-
-
- Actions
-
+
+
+ Status
+
+
+ Lorem
+
+
+ Ipsum
+
+
+ Dolor
+
+
+ Actions
+
+
{getState('rows').map((row: any) => (
@@ -592,15 +695,17 @@ const docs: ComponentDocs = {
-
- Lorem
-
-
- Ipsum
-
-
- Dolor
-
+
+
+ Lorem
+
+
+ Ipsum
+
+
+ Dolor
+
+
@@ -640,15 +745,17 @@ const docs: ComponentDocs = {
-
- Lorem
-
-
- Ipsum
-
-
- Dolor
-
+
+
+ Lorem
+
+
+ Ipsum
+
+
+ Dolor
+
+
@@ -692,7 +799,7 @@ const docs: ComponentDocs = {
The horizontal alignment of content within cells can be configured
using the align prop on TableCell{' '}
- components.
+ and TableHeadCell components.
Supported alignments are left (default),{' '}
@@ -724,15 +831,17 @@ const docs: ComponentDocs = {
])}
-
- “left”
-
-
- “center”
-
-
- “right”
-
+
+
+ “left”
+
+
+ “center”
+
+
+ “right”
+
+
{getState('rows').map((row: any) => (
@@ -764,9 +873,10 @@ const docs: ComponentDocs = {
description: (
<>
- By default, all TableCell components are prevented
- from wrapping their content. This keep rows a consistent height and
- means the content can influence the column width.
+ By default, all TableCell and{' '}
+ TableHeadCell components are prevented from
+ wrapping their content. This keep rows a consistent height and means
+ the content can influence the column width.
If desired, wrapping can be enabled by setting the{' '}
@@ -795,15 +905,17 @@ const docs: ComponentDocs = {
])}
-
- Lorem
-
-
- Ipsum
-
-
- Dolor
-
+
+
+ Lorem
+
+
+ Ipsum
+
+
+ Dolor
+
+
{getState('rows').map((row: any) => (
@@ -879,15 +991,17 @@ const docs: ComponentDocs = {
-
- Lorem
-
-
- Ipsum
-
-
- Dolor
-
+
+
+ Lorem
+
+
+ Ipsum
+
+
+ Dolor
+
+
{getState('rows').map((row: any) => (
@@ -912,12 +1026,14 @@ const docs: ComponentDocs = {
-
- Lorem
-
-
- Dolor
-
+
+
+ Lorem
+
+
+ Dolor
+
+
{getState('rows').map((row: any) => (
@@ -953,15 +1069,17 @@ const docs: ComponentDocs = {
])}
-
- Lorem
-
-
- Ipsum
-
-
- Dolor
-
+
+
+ Lorem
+
+
+ Ipsum
+
+
+ Dolor
+
+
{getState('rows').map((row: any) => (
@@ -1017,15 +1135,17 @@ const docs: ComponentDocs = {
-
- Lorem
-
-
- Ipsum
-
-
- Dolor
-
+
+
+ Lorem
+
+
+ Ipsum
+
+
+ Dolor
+
+
{getState('rows').map((row: any) => (
diff --git a/packages/braid-design-system/src/lib/components/Table/TableCell.tsx b/packages/braid-design-system/src/lib/components/Table/TableCell.tsx
index e4409e6d19b..1fec8709004 100644
--- a/packages/braid-design-system/src/lib/components/Table/TableCell.tsx
+++ b/packages/braid-design-system/src/lib/components/Table/TableCell.tsx
@@ -21,8 +21,7 @@ import buildDataAttributes, {
import * as styles from './Table.css';
type Percentage = `${number}%`;
-interface TableCellProps {
- header?: boolean;
+interface CellProps {
children: ReactNode;
hideBelow?: ResponsiveRangeProps['below'];
hideAbove?: ResponsiveRangeProps['above'];
@@ -31,10 +30,15 @@ interface TableCellProps {
width?: 'content' | 'auto' | Percentage;
minWidth?: number;
maxWidth?: number;
+ colspan?: number;
data?: DataAttributeMap;
}
-export const TableCell = ({
- header,
+interface BaseCellProps {
+ header?: boolean;
+ scope?: 'col' | 'row';
+}
+const Cell = ({
+ header: isHeaderCell,
children,
hideAbove,
hideBelow,
@@ -43,29 +47,22 @@ export const TableCell = ({
width = 'auto',
minWidth,
maxWidth,
+ colspan,
+ scope,
data,
...restProps
-}: TableCellProps) => {
+}: CellProps & BaseCellProps) => {
const [hideOnMobile, hideOnTablet, hideOnDesktop, hideOnWide] =
resolveResponsiveRangeProps({
below: hideBelow,
above: hideAbove,
});
- const tableHeaderContext = useContext(TableHeaderContext);
const tableFooterContext = useContext(TableFooterContext);
- const tableRowContext = useContext(TableRowContext);
const tableContext = useContext(TableContext);
- assert(
- tableRowContext,
- 'TableCell cannot be used outside a TableRow component',
- );
-
assert(tableContext, 'TableCell cannot be used outside a Table component');
- const isHeaderCell =
- typeof header !== 'undefined' ? header : tableHeaderContext;
const isFooterCell = tableFooterContext;
const softWidth = width === 'content' ? '1%' : width;
@@ -74,7 +71,8 @@ export const TableCell = ({
return (
.
- * However, we don't do this for the to avoid long titles forcing
- * columns to be wider than they need to be to display the data.
- * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table#displaying_large_tables_in_small_spaces
- */
[styles.nowrap]: !wrap,
[styles.softWidth]: width && width !== 'auto',
[styles.minWidth]: typeof minWidth !== 'undefined',
[styles.maxWidth]: hasMaxWidth,
[styles.alignYCenter]: tableContext.alignY === 'center',
- [styles.borderBottom]: true,
- // [styles.borderTopFooter]: isFooterCell,
+ [styles.bodyCellBorder]: !isHeaderCell,
+ [styles.footerCellBorder]: isFooterCell,
+ [styles.headerCellBorder]: isHeaderCell,
[styles.showOnTablet]: !hideOnTablet && hideOnMobile,
[styles.showOnDesktop]:
!hideOnDesktop && (hideOnTablet || hideOnMobile),
@@ -132,3 +122,34 @@ export const TableCell = ({
);
};
+
+export const TableCell = (props: CellProps) => {
+ const tableHeaderContext = useContext(TableHeaderContext);
+ const tableRowContext = useContext(TableRowContext);
+
+ assert(
+ !tableHeaderContext,
+ 'TableCell cannot be used inside a TableHeader component. Please use TableHeadCell instead.',
+ );
+
+ assert(
+ tableRowContext,
+ 'TableCell cannot be used outside a TableRow component',
+ );
+
+ return | ;
+};
+
+export const TableHeadCell = (props: CellProps) => {
+ const tableHeaderContext = useContext(TableHeaderContext);
+ const tableRowContext = useContext(TableRowContext);
+
+ assert(
+ tableRowContext,
+ 'TableHeadCell cannot be used outside a TableRow component',
+ );
+
+ return (
+ |
+ );
+};
diff --git a/packages/braid-design-system/src/lib/components/Table/TableHeader.tsx b/packages/braid-design-system/src/lib/components/Table/TableHeader.tsx
index beb4056d535..92772318354 100644
--- a/packages/braid-design-system/src/lib/components/Table/TableHeader.tsx
+++ b/packages/braid-design-system/src/lib/components/Table/TableHeader.tsx
@@ -1,13 +1,12 @@
import assert from 'assert';
import { useContext, type ReactNode } from 'react';
import { Box } from '../Box/Box';
-import { TableRow } from './TableRow';
import { TableContext, TableHeaderContext } from './TableContext';
import buildDataAttributes, {
type DataAttributeMap,
} from '../private/buildDataAttributes';
-import * as styles from './Table.css';
+// import * as styles from './Table.css';
interface TableHeaderProps {
children: ReactNode;
@@ -29,13 +28,13 @@ TableHeaderProps) => {
- {children}
+ {children}
);
diff --git a/packages/braid-design-system/src/lib/components/Table/TableRow.tsx b/packages/braid-design-system/src/lib/components/Table/TableRow.tsx
index 0c46ec890f5..1eca471828b 100644
--- a/packages/braid-design-system/src/lib/components/Table/TableRow.tsx
+++ b/packages/braid-design-system/src/lib/components/Table/TableRow.tsx
@@ -27,14 +27,12 @@ export const TableRow = ({ children, data, ...restProps }: TableRowProps) => {
assert(
!tableRowContext,
- tableHeaderContext
- ? 'TableRow is already provided by the TableHeader component'
- : 'TableRow cannot be nested instead another TableRow',
+ 'TableRow cannot be nested instead another TableRow',
);
assert(
tableBodyContext || tableHeaderContext || tableFooterContext,
- 'TableRow must be used within a TableBody component',
+ 'TableRow must be used within a table section, e.g. TableHeader, TableBody or TableFooter component',
);
return (
diff --git a/packages/braid-design-system/src/lib/components/index.ts b/packages/braid-design-system/src/lib/components/index.ts
index 550b99d9915..20f8754e3b8 100644
--- a/packages/braid-design-system/src/lib/components/index.ts
+++ b/packages/braid-design-system/src/lib/components/index.ts
@@ -73,6 +73,7 @@ export { Table } from './Table/Table';
export { TableBody } from './Table/TableBody';
export { TableCell } from './Table/TableCell';
export { TableFooter } from './Table/TableFooter';
+export { TableHeadCell } from './Table/TableCell';
export { TableHeader } from './Table/TableHeader';
export { TableRow } from './Table/TableRow';
export { Tab } from './Tabs/Tab';
diff --git a/packages/generate-component-docs/src/__snapshots__/contract.test.ts.snap b/packages/generate-component-docs/src/__snapshots__/contract.test.ts.snap
index 5e64556fe86..8a71bd88442 100644
--- a/packages/generate-component-docs/src/__snapshots__/contract.test.ts.snap
+++ b/packages/generate-component-docs/src/__snapshots__/contract.test.ts.snap
@@ -7873,7 +7873,6 @@ exports[`Table 1`] = `
| "center"
| "top"
children: ReactNode
- columnWidths?: (string | number)[]
data?: DataAttributeMap
fullBleed?: boolean
label: string
@@ -7900,8 +7899,8 @@ exports[`TableCell 1`] = `
| "left"
| "right"
children: ReactNode
+ colspan?: number
data?: DataAttributeMap
- header?: boolean
hideAbove?:
| "desktop"
| "mobile"
@@ -7910,11 +7909,52 @@ exports[`TableCell 1`] = `
| "desktop"
| "tablet"
| "wide"
- nowrap?: boolean
+ maxWidth?: number
+ minWidth?: number
width?:
| "auto"
| "content"
- | number
+ | \`\${number}%\`
+ wrap?: boolean
+},
+}
+`;
+
+exports[`TableFooter 1`] = `
+{
+ exportType: component,
+ props: {
+ children: ReactNode
+ data?: DataAttributeMap
+},
+}
+`;
+
+exports[`TableHeadCell 1`] = `
+{
+ exportType: component,
+ props: {
+ align?:
+ | "center"
+ | "left"
+ | "right"
+ children: ReactNode
+ colspan?: number
+ data?: DataAttributeMap
+ hideAbove?:
+ | "desktop"
+ | "mobile"
+ | "tablet"
+ hideBelow?:
+ | "desktop"
+ | "tablet"
+ | "wide"
+ maxWidth?: number
+ minWidth?: number
+ width?:
+ | "auto"
+ | "content"
+ | \`\${number}%\`
wrap?: boolean
},
}
diff --git a/site/src/undocumentedExports.json b/site/src/undocumentedExports.json
index 93b2e2c5d36..05f293b5dc9 100644
--- a/site/src/undocumentedExports.json
+++ b/site/src/undocumentedExports.json
@@ -15,6 +15,7 @@
"TableBody",
"TableCell",
"TableFooter",
+ "TableHeadCell",
"TableHeader",
"TableRow",
"Tab",