Merge pull request #31917 from backstage/bui-table-cell

Bui table cell
This commit is contained in:
Charles de Dreuille
2025-12-01 08:42:34 +00:00
committed by GitHub
12 changed files with 451 additions and 203 deletions
+37
View File
@@ -0,0 +1,37 @@
---
'@backstage/ui': minor
---
**Breaking change** The `Cell` component has been refactored to be a generic wrapper component that accepts `children` for custom cell content. The text-specific functionality (previously part of `Cell`) has been moved to a new `CellText` component.
### Migration Guide
If you were using `Cell` with text-specific props (`title`, `description`, `leadingIcon`, `href`), you need to update your code to use `CellText` instead:
**Before:**
```tsx
<Cell
title="My Title"
description="My description"
leadingIcon={<Icon />}
href="/path"
/>
```
**After:**
```tsx
<CellText
title="My Title"
description="My description"
leadingIcon={<Icon />}
href="/path"
/>
```
For custom cell content, use the new generic `Cell` component:
```tsx
<Cell>{/* Your custom content */}</Cell>
```
+11 -11
View File
@@ -267,7 +267,7 @@ export const tablePaginationPropDefs: Record<string, PropDef> = {
...stylePropDefs,
};
export const tableUsageSnippet = `import { Cell, ..., TableHeader, TablePagination } from '@backstage/ui';
export const tableUsageSnippet = `import { Cell, CellText, ..., TableHeader, TablePagination } from '@backstage/ui';
<Table>
<TableHeader>
@@ -275,14 +275,14 @@ export const tableUsageSnippet = `import { Cell, ..., TableHeader, TablePaginati
</TableHeader>
<TableBody>
<Row>
<Cell />
<CellText title="Example" />
<CellProfile />
</Row>
</TableBody>
</Table>
<TablePagination />`;
export const tableBasicSnippet = `import { Table, TableHeader, Column, TableBody, Row, Cell, CellProfile, TablePagination, useTable } from '@backstage/ui';
export const tableBasicSnippet = `import { Table, TableHeader, Column, TableBody, Row, CellText, CellProfile, TablePagination, useTable } from '@backstage/ui';
const data = [
{
@@ -318,16 +318,16 @@ const { data: paginatedData, paginationProps } = useTable({
src={item.image}
href={item.website}
/>
<Cell title={item.genre} />
<Cell title={item.yearFormed.toString()} />
<Cell title={item.albums.toString()} />
<CellText title={item.genre} />
<CellText title={item.yearFormed.toString()} />
<CellText title={item.albums.toString()} />
</Row>
))}
</TableBody>
</Table>
<TablePagination {...paginationProps} />`;
export const tableSelectionActionsSnippet = `import { Table, TableHeader, TableBody, Column, Row, Cell } from '@backstage/ui';
export const tableSelectionActionsSnippet = `import { Table, TableHeader, TableBody, Column, Row, CellText } from '@backstage/ui';
function MyTable() {
const [selectedKeys, setSelectedKeys] = React.useState(new Set([]));
@@ -346,12 +346,12 @@ function MyTable() {
</TableHeader>
<TableBody>
<Row id="1">
<Cell title="Component A" />
<Cell title="Active" />
<CellText title="Component A" />
<CellText title="Active" />
</Row>
<Row id="2">
<Cell title="Component B" />
<Cell title="Inactive" />
<CellText title="Component B" />
<CellText title="Inactive" />
</Row>
</TableBody>
</Table>
+14 -1
View File
@@ -406,7 +406,16 @@ export interface CellProfileProps extends CellProps_2 {
}
// @public (undocumented)
export interface CellProps extends CellProps_2 {
export interface CellProps extends CellProps_2 {}
// @public (undocumented)
export const CellText: {
(props: CellTextProps): JSX_2.Element;
displayName: string;
};
// @public (undocumented)
export interface CellTextProps extends CellProps_2 {
// (undocumented)
color?: TextColors;
// (undocumented)
@@ -863,12 +872,16 @@ export const LinkDefinition: {
// @public (undocumented)
export interface LinkProps extends LinkProps_2 {
// (undocumented)
children?: ReactNode;
// (undocumented)
color?:
| TextColors
| TextColorStatus
| Partial<Record<Breakpoint, TextColors | TextColorStatus>>;
// (undocumented)
title?: string;
// (undocumented)
truncate?: boolean;
// (undocumented)
variant?: TextVariants | Partial<Record<Breakpoint, TextVariants>>;
+43 -14
View File
@@ -14,8 +14,9 @@
* limitations under the License.
*/
import { forwardRef } from 'react';
import { Link as AriaLink, RouterProvider } from 'react-aria-components';
import { forwardRef, useRef } from 'react';
import { useLink } from 'react-aria';
import { RouterProvider } from 'react-aria-components';
import clsx from 'clsx';
import { useStyles } from '../../hooks/useStyles';
import { LinkDefinition } from './definition';
@@ -37,29 +38,57 @@ export const Link = forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => {
},
);
const { className, href, ...restProps } = cleanedProps;
const {
className,
href,
title,
children,
onPress,
variant,
weight,
color,
truncate,
slot,
...restProps
} = cleanedProps;
const isExternal = isExternalLink(href);
const internalRef = useRef<HTMLAnchorElement>(null);
const linkRef = (ref || internalRef) as React.RefObject<HTMLAnchorElement>;
const component = (
<AriaLink
ref={ref}
className={clsx(classNames.root, styles[classNames.root], className)}
href={href}
{...dataAttributes}
{...restProps}
/>
// Use useLink hook to get link props
const { linkProps } = useLink(
{
href,
onPress,
...restProps,
},
linkRef,
);
// If it's an external link, render AriaLink without RouterProvider
const anchorElement = (
<a
{...linkProps}
{...dataAttributes}
{...(restProps as React.AnchorHTMLAttributes<HTMLAnchorElement>)}
ref={linkRef}
href={href}
title={title}
className={clsx(classNames.root, styles[classNames.root], className)}
>
{children}
</a>
);
// If it's an external link, render without RouterProvider
if (isExternal) {
return component;
return anchorElement;
}
// For internal links, use RouterProvider
return (
<RouterProvider navigate={navigate} useHref={useHref}>
{component}
{anchorElement}
</RouterProvider>
);
});
+7
View File
@@ -22,6 +22,7 @@ import type {
TextWeights,
} from '../../types';
import type { LinkProps as AriaLinkProps } from 'react-aria-components';
import type { ReactNode } from 'react';
/** @public */
export interface LinkProps extends AriaLinkProps {
@@ -32,4 +33,10 @@ export interface LinkProps extends AriaLinkProps {
| TextColorStatus
| Partial<Record<Breakpoint, TextColors | TextColorStatus>>;
truncate?: boolean;
// This is used to set the title attribute on the link
title?: string;
// This is used to set the children of the link
children?: ReactNode;
}
@@ -21,6 +21,7 @@
width: 100%;
caption-side: bottom;
border-collapse: collapse;
table-layout: fixed;
}
.bui-TableHeader {
@@ -109,6 +110,10 @@
.bui-TableCell {
padding: var(--bui-space-3);
font-size: var(--bui-font-size-3);
font-family: var(--bui-font-regular);
font-weight: var(--bui-font-weight-regular);
min-width: 0;
overflow: hidden;
}
.bui-TableCellSelection {
@@ -116,10 +121,13 @@
}
.bui-TableCellContentWrapper {
display: inline-flex;
display: flex;
flex-direction: row;
align-items: center;
gap: var(--bui-space-2);
min-width: 0;
width: 100%;
max-width: 100%;
}
.bui-TableCellIcon,
@@ -133,6 +141,15 @@
display: flex;
flex-direction: column;
gap: var(--bui-space-0_5);
min-width: 0;
flex: 1;
overflow: hidden;
max-width: 100%;
}
.bui-TableCellContent > * {
min-width: 0;
max-width: 100%;
}
.bui-TableCellProfile {
+169 -135
View File
@@ -24,7 +24,8 @@ import {
TableBody,
Row,
Cell,
CellProfile as CellProfileBUI,
CellText,
CellProfile,
useTable,
} from '.';
import { RadioGroup, Radio } from '../RadioGroup';
@@ -69,18 +70,18 @@ export const TableOnly: Story = {
<TableBody>
{data1.map(item => (
<Row key={item.name}>
<Cell
<CellText
title={item.name}
leadingIcon={<RiCactusLine />}
description={item.description}
/>
<CellProfileBUI
<CellProfile
name={item.owner.name}
src={item.owner.profilePicture}
href={item.owner.link}
/>
<Cell title={item.type} />
<Cell title={item.lifecycle} />
<CellText title={item.type} />
<CellText title={item.lifecycle} />
</Row>
))}
</TableBody>
@@ -105,14 +106,14 @@ export const WithPaginationUncontrolled: Story = {
<TableBody>
{data?.map(item => (
<Row key={item.name}>
<Cell
<CellText
title={item.name}
leadingIcon={<RiCactusLine />}
description={item.description}
/>
<Cell title={item.owner.name} />
<Cell title={item.type} />
<Cell title={item.lifecycle} />
<CellText title={item.owner.name} />
<CellText title={item.type} />
<CellText title={item.lifecycle} />
</Row>
))}
</TableBody>
@@ -152,14 +153,14 @@ export const WithPaginationControlled: Story = {
<TableBody>
{data?.map(item => (
<Row key={item.name}>
<CellProfileBUI
<CellProfile
name={item.name}
src={item.image}
href={item.website}
/>
<Cell title={item.genre} />
<Cell title={item.yearFormed.toString()} />
<Cell title={item.albums.toString()} />
<CellText title={item.genre} />
<CellText title={item.yearFormed.toString()} />
<CellText title={item.albums.toString()} />
</Row>
))}
</TableBody>
@@ -188,18 +189,18 @@ export const Sorting: Story = {
<TableBody>
{data1.map(item => (
<Row key={item.name}>
<Cell
<CellText
title={item.name}
leadingIcon={<RiCactusLine />}
description={item.description}
/>
<CellProfileBUI
<CellProfile
name={item.owner.name}
src={item.owner.profilePicture}
href={item.owner.link}
/>
<Cell title={item.type} />
<Cell title={item.lifecycle} />
<CellText title={item.type} />
<CellText title={item.lifecycle} />
</Row>
))}
</TableBody>
@@ -229,14 +230,14 @@ export const TableRockBand: Story = {
<TableBody>
{data?.map(item => (
<Row key={item.name}>
<CellProfileBUI
<CellProfile
name={item.name}
src={item.image}
href={item.website}
/>
<Cell title={item.genre} />
<Cell title={item.yearFormed.toString()} />
<Cell title={item.albums.toString()} />
<CellText title={item.genre} />
<CellText title={item.yearFormed.toString()} />
<CellText title={item.albums.toString()} />
</Row>
))}
</TableBody>
@@ -268,14 +269,14 @@ export const RowClick: Story = {
<TableBody>
{data?.map(item => (
<Row key={item.name} onAction={() => alert('Row clicked')}>
<CellProfileBUI
<CellProfile
name={item.name}
src={item.image}
href={item.website}
/>
<Cell title={item.genre} />
<Cell title={item.yearFormed.toString()} />
<Cell title={item.albums.toString()} />
<CellText title={item.genre} />
<CellText title={item.yearFormed.toString()} />
<CellText title={item.albums.toString()} />
</Row>
))}
</TableBody>
@@ -307,14 +308,14 @@ export const RowLink: Story = {
<TableBody>
{data?.map(item => (
<Row key={item.name} href="/band">
<CellProfileBUI
<CellProfile
name={item.name}
src={item.image}
href={item.website}
/>
<Cell title={item.genre} />
<Cell title={item.yearFormed.toString()} />
<Cell title={item.albums.toString()} />
<CellText title={item.genre} />
<CellText title={item.yearFormed.toString()} />
<CellText title={item.albums.toString()} />
</Row>
))}
</TableBody>
@@ -325,7 +326,38 @@ export const RowLink: Story = {
},
};
export const CellText: Story = {
export const CellComponent: Story = {
name: 'Cell',
render: () => {
return (
<Table>
<TableHeader>
<Column isRowHeader>Name</Column>
</TableHeader>
<TableBody>
<Row>
<Cell>Hello world</Cell>
</Row>
<Row>
<Cell>
This is a very long text that demonstrates how the Cell component
handles lengthy content. It should wrap appropriately and maintain
proper styling even when the text extends beyond the normal cell
width. This helps ensure that the table remains readable and
visually consistent regardless of the content length.
</Cell>
</Row>
<Row>
<Cell>Hello world</Cell>
</Row>
</TableBody>
</Table>
);
},
};
export const CellTextComponent: Story = {
name: 'CellText',
render: () => {
return (
<Table>
@@ -335,10 +367,11 @@ export const CellText: Story = {
<TableBody>
{data2.map(item => (
<Row key={item.name}>
<Cell
<CellText
title={item.name}
leadingIcon={item.icon}
description={item.description}
href={item.href}
/>
</Row>
))}
@@ -348,7 +381,8 @@ export const CellText: Story = {
},
};
export const CellProfile: Story = {
export const CellProfileComponent: Story = {
name: 'CellProfile',
render: () => {
return (
<Table>
@@ -358,7 +392,7 @@ export const CellProfile: Story = {
<TableBody>
{data3.map(item => (
<Row key={item.name}>
<CellProfileBUI
<CellProfile
name={item.name}
src={item.profilePicture}
href={item.link}
@@ -390,19 +424,19 @@ export const SelectionSingleToggle: Story = {
</TableHeader>
<TableBody>
<Row id="1">
<Cell title="Component Library" />
<Cell title="Design System" />
<Cell title="library" />
<CellText title="Component Library" />
<CellText title="Design System" />
<CellText title="library" />
</Row>
<Row id="2">
<Cell title="API Gateway" />
<Cell title="Platform" />
<Cell title="service" />
<CellText title="API Gateway" />
<CellText title="Platform" />
<CellText title="service" />
</Row>
<Row id="3">
<Cell title="Documentation Site" />
<Cell title="DevEx" />
<Cell title="website" />
<CellText title="Documentation Site" />
<CellText title="DevEx" />
<CellText title="website" />
</Row>
</TableBody>
</Table>
@@ -428,19 +462,19 @@ export const SelectionMultiToggle: Story = {
</TableHeader>
<TableBody>
<Row id="1">
<Cell title="Component Library" />
<Cell title="Design System" />
<Cell title="library" />
<CellText title="Component Library" />
<CellText title="Design System" />
<CellText title="library" />
</Row>
<Row id="2">
<Cell title="API Gateway" />
<Cell title="Platform" />
<Cell title="service" />
<CellText title="API Gateway" />
<CellText title="Platform" />
<CellText title="service" />
</Row>
<Row id="3">
<Cell title="Documentation Site" />
<Cell title="DevEx" />
<Cell title="website" />
<CellText title="Documentation Site" />
<CellText title="DevEx" />
<CellText title="website" />
</Row>
</TableBody>
</Table>
@@ -466,19 +500,19 @@ export const SelectionSingleReplace: Story = {
</TableHeader>
<TableBody>
<Row id="1">
<Cell title="Component Library" />
<Cell title="Design System" />
<Cell title="library" />
<CellText title="Component Library" />
<CellText title="Design System" />
<CellText title="library" />
</Row>
<Row id="2">
<Cell title="API Gateway" />
<Cell title="Platform" />
<Cell title="service" />
<CellText title="API Gateway" />
<CellText title="Platform" />
<CellText title="service" />
</Row>
<Row id="3">
<Cell title="Documentation Site" />
<Cell title="DevEx" />
<Cell title="website" />
<CellText title="Documentation Site" />
<CellText title="DevEx" />
<CellText title="website" />
</Row>
</TableBody>
</Table>
@@ -504,19 +538,19 @@ export const SelectionMultiReplace: Story = {
</TableHeader>
<TableBody>
<Row id="1">
<Cell title="Component Library" />
<Cell title="Design System" />
<Cell title="library" />
<CellText title="Component Library" />
<CellText title="Design System" />
<CellText title="library" />
</Row>
<Row id="2">
<Cell title="API Gateway" />
<Cell title="Platform" />
<Cell title="service" />
<CellText title="API Gateway" />
<CellText title="Platform" />
<CellText title="service" />
</Row>
<Row id="3">
<Cell title="Documentation Site" />
<Cell title="DevEx" />
<Cell title="website" />
<CellText title="Documentation Site" />
<CellText title="DevEx" />
<CellText title="website" />
</Row>
</TableBody>
</Table>
@@ -543,19 +577,19 @@ export const SelectionToggleWithActions: Story = {
</TableHeader>
<TableBody>
<Row id="1">
<Cell title="Component Library" />
<Cell title="Design System" />
<Cell title="library" />
<CellText title="Component Library" />
<CellText title="Design System" />
<CellText title="library" />
</Row>
<Row id="2">
<Cell title="API Gateway" />
<Cell title="Platform" />
<Cell title="service" />
<CellText title="API Gateway" />
<CellText title="Platform" />
<CellText title="service" />
</Row>
<Row id="3">
<Cell title="Documentation Site" />
<Cell title="DevEx" />
<Cell title="website" />
<CellText title="Documentation Site" />
<CellText title="DevEx" />
<CellText title="website" />
</Row>
</TableBody>
</Table>
@@ -582,19 +616,19 @@ export const SelectionReplaceWithActions: Story = {
</TableHeader>
<TableBody>
<Row id="1">
<Cell title="Component Library" />
<Cell title="Design System" />
<Cell title="library" />
<CellText title="Component Library" />
<CellText title="Design System" />
<CellText title="library" />
</Row>
<Row id="2">
<Cell title="API Gateway" />
<Cell title="Platform" />
<Cell title="service" />
<CellText title="API Gateway" />
<CellText title="Platform" />
<CellText title="service" />
</Row>
<Row id="3">
<Cell title="Documentation Site" />
<Cell title="DevEx" />
<Cell title="website" />
<CellText title="Documentation Site" />
<CellText title="DevEx" />
<CellText title="website" />
</Row>
</TableBody>
</Table>
@@ -620,19 +654,19 @@ export const SelectionToggleWithLinks: Story = {
</TableHeader>
<TableBody>
<Row id="1" href="https://example.com/library">
<Cell title="Component Library" />
<Cell title="Design System" />
<Cell title="library" />
<CellText title="Component Library" />
<CellText title="Design System" />
<CellText title="library" />
</Row>
<Row id="2" href="https://example.com/gateway">
<Cell title="API Gateway" />
<Cell title="Platform" />
<Cell title="service" />
<CellText title="API Gateway" />
<CellText title="Platform" />
<CellText title="service" />
</Row>
<Row id="3" href="https://example.com/docs">
<Cell title="Documentation Site" />
<Cell title="DevEx" />
<Cell title="website" />
<CellText title="Documentation Site" />
<CellText title="DevEx" />
<CellText title="website" />
</Row>
</TableBody>
</Table>
@@ -658,19 +692,19 @@ export const SelectionReplaceWithLinks: Story = {
</TableHeader>
<TableBody>
<Row id="1" href="https://example.com/library">
<Cell title="Component Library" />
<Cell title="Design System" />
<Cell title="library" />
<CellText title="Component Library" />
<CellText title="Design System" />
<CellText title="library" />
</Row>
<Row id="2" href="https://example.com/gateway">
<Cell title="API Gateway" />
<Cell title="Platform" />
<Cell title="service" />
<CellText title="API Gateway" />
<CellText title="Platform" />
<CellText title="service" />
</Row>
<Row id="3" href="https://example.com/docs">
<Cell title="Documentation Site" />
<Cell title="DevEx" />
<Cell title="website" />
<CellText title="Documentation Site" />
<CellText title="DevEx" />
<CellText title="website" />
</Row>
</TableBody>
</Table>
@@ -697,19 +731,19 @@ export const SelectionWithDisabledRows: Story = {
</TableHeader>
<TableBody>
<Row id="1">
<Cell title="Component Library" />
<Cell title="Design System" />
<Cell title="library" />
<CellText title="Component Library" />
<CellText title="Design System" />
<CellText title="library" />
</Row>
<Row id="2">
<Cell title="API Gateway (Disabled)" />
<Cell title="Platform" />
<Cell title="service" />
<CellText title="API Gateway (Disabled)" />
<CellText title="Platform" />
<CellText title="service" />
</Row>
<Row id="3">
<Cell title="Documentation Site" />
<Cell title="DevEx" />
<Cell title="website" />
<CellText title="Documentation Site" />
<CellText title="DevEx" />
<CellText title="website" />
</Row>
</TableBody>
</Table>
@@ -744,9 +778,9 @@ export const SelectionWithPagination: Story = {
<TableBody>
{data?.map(item => (
<Row key={item.name} id={item.name}>
<Cell title={item.name} />
<Cell title={item.owner.name} />
<Cell title={item.type} />
<CellText title={item.name} />
<CellText title={item.owner.name} />
<CellText title={item.type} />
</Row>
))}
</TableBody>
@@ -779,19 +813,19 @@ export const SelectionModePlayground: Story = {
</TableHeader>
<TableBody>
<Row id="1">
<Cell title="Component Library" />
<Cell title="Design System" />
<Cell title="library" />
<CellText title="Component Library" />
<CellText title="Design System" />
<CellText title="library" />
</Row>
<Row id="2">
<Cell title="API Gateway" />
<Cell title="Platform" />
<Cell title="service" />
<CellText title="API Gateway" />
<CellText title="Platform" />
<CellText title="service" />
</Row>
<Row id="3">
<Cell title="Documentation Site" />
<Cell title="DevEx" />
<Cell title="website" />
<CellText title="Documentation Site" />
<CellText title="DevEx" />
<CellText title="website" />
</Row>
</TableBody>
</Table>
@@ -839,19 +873,19 @@ export const SelectionBehaviorPlayground: Story = {
</TableHeader>
<TableBody>
<Row id="1">
<Cell title="Component Library" />
<Cell title="Design System" />
<Cell title="library" />
<CellText title="Component Library" />
<CellText title="Design System" />
<CellText title="library" />
</Row>
<Row id="2">
<Cell title="API Gateway" />
<Cell title="Platform" />
<Cell title="service" />
<CellText title="API Gateway" />
<CellText title="Platform" />
<CellText title="service" />
</Row>
<Row id="3">
<Cell title="Documentation Site" />
<Cell title="DevEx" />
<Cell title="website" />
<CellText title="Documentation Site" />
<CellText title="DevEx" />
<CellText title="website" />
</Row>
</TableBody>
</Table>
@@ -15,8 +15,6 @@
*/
import clsx from 'clsx';
import { Text } from '../../Text';
import { Link } from '../../Link';
import { Cell as ReactAriaCell } from 'react-aria-components';
import type { CellProps } from '../types';
import { useStyles } from '../../../hooks/useStyles';
@@ -29,49 +27,14 @@ const Cell = (props: CellProps) => {
color: 'primary' as const,
...props,
});
const { className, title, description, color, leadingIcon, href, ...rest } =
cleanedProps;
const { className, children, ...rest } = cleanedProps;
return (
<ReactAriaCell
className={clsx(classNames.cell, styles[classNames.cell], className)}
{...rest}
>
<div
className={clsx(
classNames.cellContentWrapper,
styles[classNames.cellContentWrapper],
)}
>
{leadingIcon && (
<div
className={clsx(classNames.cellIcon, styles[classNames.cellIcon])}
>
{leadingIcon}
</div>
)}
<div
className={clsx(
classNames.cellContent,
styles[classNames.cellContent],
)}
>
{href ? (
<Link href={href} variant="body-medium" color={color}>
{title}
</Link>
) : (
<Text as="p" variant="body-medium" color={color}>
{title}
</Text>
)}
{description && (
<Text variant="body-medium" color="secondary">
{description}
</Text>
)}
</div>
</div>
{children}
</ReactAriaCell>
);
};
@@ -0,0 +1,98 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import clsx from 'clsx';
import { Text } from '../../Text';
import { Link } from '../../Link';
import { Cell as ReactAriaCell } from 'react-aria-components';
import type { CellTextProps } from '../types';
import { useStyles } from '../../../hooks/useStyles';
import { TableDefinition } from '../definition';
import styles from '../Table.module.css';
/** @public */
const CellText = (props: CellTextProps) => {
const { classNames, cleanedProps } = useStyles(TableDefinition, {
color: 'primary' as const,
...props,
});
const { className, title, description, color, leadingIcon, href, ...rest } =
cleanedProps;
return (
<ReactAriaCell
className={clsx(classNames.cell, styles[classNames.cell], className)}
{...rest}
>
<div
className={clsx(
classNames.cellContentWrapper,
styles[classNames.cellContentWrapper],
)}
>
{leadingIcon && (
<div
className={clsx(classNames.cellIcon, styles[classNames.cellIcon])}
>
{leadingIcon}
</div>
)}
<div
className={clsx(
classNames.cellContent,
styles[classNames.cellContent],
)}
>
{href ? (
<Link
href={href}
variant="body-medium"
color={color}
truncate
title={title}
>
{title}
</Link>
) : (
<Text
as="p"
variant="body-medium"
color={color}
truncate
title={title}
>
{title}
</Text>
)}
{description && (
<Text
variant="body-medium"
color="secondary"
truncate
title={description}
>
{description}
</Text>
)}
</div>
</div>
</ReactAriaCell>
);
};
CellText.displayName = 'CellText';
export { CellText };
+7 -1
View File
@@ -20,10 +20,16 @@ export { TableBody } from './components/TableBody';
export { Column } from './components/Column';
export { Row } from './components/Row';
export { Cell } from './components/Cell';
export { CellText } from './components/CellText';
export { CellProfile } from './components/CellProfile';
export { useTable } from './hooks/useTable';
export type { CellProps, CellProfileProps, ColumnProps } from './types';
export type {
CellProps,
CellTextProps,
CellProfileProps,
ColumnProps,
} from './types';
export type {
UseTableConfig,
UseTableResult,
@@ -21,6 +21,7 @@ export interface DataProps {
name: string;
description?: string;
icon?: React.ReactNode;
href?: string;
}
export const data: DataProps[] = [
@@ -43,4 +44,44 @@ export const data: DataProps[] = [
'A comprehensive service handling user authentication and role-based access control across all applications.',
icon: createElement(RiCactusLine),
},
{
icon: createElement(RiCactusLine),
name: 'Cell with extremely long title that demonstrates text wrapping behavior and how the component handles content that extends well beyond typical cell widths in table layouts',
description:
'This is a comprehensive and detailed description that contains a substantial amount of text to demonstrate how the CellText component handles lengthy content. It includes multiple sentences and covers various aspects of the service or component being described. The text should wrap appropriately within the cell boundaries while maintaining readability and proper spacing. This example helps ensure that the table remains visually consistent and user-friendly even when dealing with extensive content that might otherwise break the layout or cause accessibility issues. The component should gracefully handle such scenarios by providing appropriate text wrapping, maintaining proper line heights, and ensuring that all content remains accessible and readable regardless of its length.',
},
{
name: 'Cell with title and URL',
href: '/catalog/default/service/auth-service',
},
{
name: 'Cell with title, description and URL',
description:
'A comprehensive service handling user authentication and role-based access control across all applications.',
href: '/catalog/default/service/auth-service',
},
{
name: 'Cell with title, icon and URL',
icon: createElement(RiCactusLine),
href: '/catalog/default/service/auth-service',
},
{
name: 'Cell with title, description, icon and URL',
description:
'A comprehensive service handling user authentication and role-based access control across all applications.',
icon: createElement(RiCactusLine),
href: '/catalog/default/service/auth-service',
},
{
icon: createElement(RiCactusLine),
name: 'Cell with extremely long title that demonstrates text wrapping behavior and how the component handles content that extends well beyond typical cell widths in table layouts',
description:
'This is a comprehensive and detailed description that contains a substantial amount of text to demonstrate how the CellText component handles lengthy content. It includes multiple sentences and covers various aspects of the service or component being described. The text should wrap appropriately within the cell boundaries while maintaining readability and proper spacing. This example helps ensure that the table remains visually consistent and user-friendly even when dealing with extensive content that might otherwise break the layout or cause accessibility issues. The component should gracefully handle such scenarios by providing appropriate text wrapping, maintaining proper line heights, and ensuring that all content remains accessible and readable regardless of its length.',
href: '/catalog/default/service/auth-service',
},
{
name: 'External link example',
description: 'This is an external link that opens in a new tab',
href: 'https://backstage.io',
},
];
+4 -1
View File
@@ -21,7 +21,10 @@ import {
import type { TextColors } from '../../types';
/** @public */
export interface CellProps extends ReactAriaCellProps {
export interface CellProps extends ReactAriaCellProps {}
/** @public */
export interface CellTextProps extends ReactAriaCellProps {
title: string;
description?: string;
color?: TextColors;