Merge pull request #33379 from backstage/rugvip/document-table-cell-requirement

ui: document table cell wrapper requirement
This commit is contained in:
Patrik Oldsberg
2026-03-16 21:18:41 +01:00
committed by GitHub
5 changed files with 117 additions and 17 deletions
@@ -0,0 +1,5 @@
---
'@backstage/ui': patch
---
Added documentation for the table cell wrapper requirement to TSDoc comments for `Cell`, `CellText`, `CellProfile`, `ColumnConfig`, and `RowRenderFn`.
+13
View File
@@ -42,6 +42,7 @@ import {
tableCombinedSnippet,
tableCustomRowSnippet,
tablePrimitivesSnippet,
tableCellRequirementSnippet,
} from './snippets';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { PageTitle } from '@/components/PageTitle';
@@ -94,6 +95,18 @@ For full control over state, use the controlled props instead:
- `search` / `onSearchChange`
- `filter` / `onFilterChange`
### Cell Requirement
Every cell rendered via `ColumnConfig.cell` (or inside a custom `RowRenderFn`) **must** return a cell component as the top-level element. The available cell components are:
- **`CellText`** — displays a title with optional description and icon.
- **`CellProfile`** — displays an avatar with a name and optional description.
- **`Cell`** — a generic wrapper for fully custom cell content.
Returning bare text, React fragments, or other elements without wrapping them in one of these cell components will break the table layout.
<CodeBlock code={tableCellRequirementSnippet} />
## Common Patterns
### Sorting
@@ -57,6 +57,23 @@ const {
filter, // { value, onChange } for filters
} = useTable({ ... });`;
// =============================================================================
// Cell Requirement
// =============================================================================
export const tableCellRequirementSnippet = `// ✅ Correct — CellText is the top-level element
cell: item => <CellText title={item.name} />;
// ✅ Correct — Cell wraps custom content
cell: item => (
<Cell>
<MyCustomContent value={item.name} />
</Cell>
);
// ❌ Wrong — bare text without a cell wrapper
cell: item => <span>{item.name}</span>;`;
// =============================================================================
// Common Patterns
// =============================================================================
+8 -9
View File
@@ -766,7 +766,7 @@ export const Cell: {
displayName: string;
};
// @public (undocumented)
// @public
export type CellOwnProps = {
className?: string;
};
@@ -774,7 +774,7 @@ export type CellOwnProps = {
// @public (undocumented)
export const CellProfile: (props: CellProfileProps) => JSX_2.Element;
// @public (undocumented)
// @public
export type CellProfileOwnProps = {
src?: string;
name?: string;
@@ -784,12 +784,12 @@ export type CellProfileOwnProps = {
className?: string;
};
// @public (undocumented)
// @public
export interface CellProfileProps
extends CellProfileOwnProps,
Omit<CellProps_2, keyof CellProfileOwnProps> {}
// @public (undocumented)
// @public
export interface CellProps
extends CellOwnProps,
Omit<CellProps_2, keyof CellOwnProps> {}
@@ -800,7 +800,7 @@ export const CellText: {
displayName: string;
};
// @public (undocumented)
// @public
export type CellTextOwnProps = {
title: string;
description?: string;
@@ -810,7 +810,7 @@ export type CellTextOwnProps = {
className?: string;
};
// @public (undocumented)
// @public
export interface CellTextProps
extends CellTextOwnProps,
Omit<CellProps_2, keyof CellTextOwnProps> {}
@@ -849,9 +849,8 @@ export interface CheckboxProps
// @public (undocumented)
export const Column: (props: ColumnProps) => JSX_2.Element;
// @public (undocumented)
// @public
export interface ColumnConfig<T extends TableItem> {
// (undocumented)
cell: (item: T) => ReactElement;
// (undocumented)
defaultWidth?: ColumnSize | null;
@@ -2068,7 +2067,7 @@ export interface RowProps<T>
extends RowOwnProps<T>,
Omit<RowProps_2<T>, keyof RowOwnProps> {}
// @public (undocumented)
// @public
export type RowRenderFn<T extends TableItem> = (params: {
item: T;
index: number;
+74 -8
View File
@@ -93,17 +93,35 @@ export interface ColumnProps
extends ColumnOwnProps,
Omit<ReactAriaColumnProps, keyof ColumnOwnProps> {}
/** @public */
/**
* Own props for the {@link Cell} component.
*
* @public
*/
export type CellOwnProps = {
className?: string;
};
/** @public */
/**
* Props for the {@link Cell} component.
*
* `Cell` is a generic cell wrapper for custom cell content. When rendering
* cells via {@link ColumnConfig.cell} or a custom {@link RowRenderFn}, the
* returned element **must** be a cell component (`Cell`, `CellText`, or
* `CellProfile`) at the top level. Returning bare text or other elements
* without a cell wrapper will break the table layout.
*
* @public
*/
export interface CellProps
extends CellOwnProps,
Omit<ReactAriaCellProps, keyof CellOwnProps> {}
/** @public */
/**
* Own props for the {@link CellText} component.
*
* @public
*/
export type CellTextOwnProps = {
title: string;
description?: string;
@@ -113,12 +131,25 @@ export type CellTextOwnProps = {
className?: string;
};
/** @public */
/**
* Props for the {@link CellText} component.
*
* `CellText` renders a table cell with a title and optional description. It
* is one of the cell components (`Cell`, `CellText`, `CellProfile`) that
* **must** be used as the top-level element returned from
* {@link ColumnConfig.cell} or a custom {@link RowRenderFn}.
*
* @public
*/
export interface CellTextProps
extends CellTextOwnProps,
Omit<ReactAriaCellProps, keyof CellTextOwnProps> {}
/** @public */
/**
* Own props for the {@link CellProfile} component.
*
* @public
*/
export type CellProfileOwnProps = {
src?: string;
name?: string;
@@ -128,7 +159,16 @@ export type CellProfileOwnProps = {
className?: string;
};
/** @public */
/**
* Props for the {@link CellProfile} component.
*
* `CellProfile` renders a table cell with an avatar, name, and optional
* description. It is one of the cell components (`Cell`, `CellText`,
* `CellProfile`) that **must** be used as the top-level element returned
* from {@link ColumnConfig.cell} or a custom {@link RowRenderFn}.
*
* @public
*/
export interface CellProfileProps
extends CellProfileOwnProps,
Omit<ReactAriaCellProps, keyof CellProfileOwnProps> {}
@@ -151,10 +191,27 @@ export interface PagePagination extends TablePaginationProps {
/** @public */
export type TablePaginationType = NoPagination | PagePagination;
/** @public */
/**
* Configuration for a single table column.
*
* @public
*/
export interface ColumnConfig<T extends TableItem> {
id: string;
label: string;
/**
* Renders the cell content for this column.
*
* **Important:** The returned element **must** be a cell component at the
* top level either `Cell`, `CellText`, or `CellProfile`. Returning bare
* text, fragments, or other elements without a cell wrapper will break the
* table layout.
*
* @example
* ```tsx
* cell: item => <CellText title={item.name} />
* ```
*/
cell: (item: T) => ReactElement;
header?: () => ReactElement;
isSortable?: boolean;
@@ -173,7 +230,16 @@ export interface RowConfig<T extends TableItem> {
getIsDisabled?: (item: T) => boolean;
}
/** @public */
/**
* Custom render function for table rows.
*
* When using a custom row render function, each cell rendered inside the row
* **must** use a cell component (`Cell`, `CellText`, or `CellProfile`) as
* the top-level element. Returning bare text or other elements without a
* cell wrapper will break the table layout.
*
* @public
*/
export type RowRenderFn<T extends TableItem> = (params: {
item: T;
index: number;