Table restructure
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
+132
-79
@@ -9523,6 +9523,125 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
gap: var(--bui-space-1);
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort .bui-DataTableRoot--unsorted {
|
||||
opacity: 0;
|
||||
transition: opacity .1s ease-in-out, transform .1s ease-in-out;
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort:hover .bui-DataTableRoot--unsorted, .bui-DataTableRoot--sort:focus .bui-DataTableRoot--unsorted {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort .bui-DataTableRoot--sorted-asc, .bui-DataTableRoot--sort:hover .bui-DataTableRoot--sorted-asc, .bui-DataTableRoot--sort:focus .bui-DataTableRoot--sorted-asc {
|
||||
opacity: 1;
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
.bui-DataTableRoot--sort .bui-DataTableRoot--sorted-desc, .bui-DataTableRoot--sort:hover .bui-DataTableRoot--sorted-desc, .bui-DataTableRoot--sort:focus .bui-DataTableRoot--sorted-desc {
|
||||
opacity: 1;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.bui-TableRoot {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bui-TableHead {
|
||||
text-align: left;
|
||||
padding: var(--bui-space-3);
|
||||
font-size: var(--bui-font-size-3);
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-TableBody {
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-TableRow {
|
||||
border-bottom: 1px solid var(--bui-border);
|
||||
transition: color .2s ease-in-out;
|
||||
}
|
||||
|
||||
.bui-TableBody .bui-TableRow:hover {
|
||||
background-color: var(--bui-gray-2);
|
||||
}
|
||||
|
||||
.bui-TableCell {
|
||||
padding: var(--bui-space-3);
|
||||
font-size: var(--bui-font-size-3);
|
||||
}
|
||||
|
||||
.bui-TableCellText {
|
||||
align-items: center;
|
||||
gap: var(--bui-space-2);
|
||||
flex-direction: row;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.bui-TableCellTextIcon, .bui-TableCellTextIcon svg {
|
||||
color: var(--bui-fg-primary);
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.bui-TableCellTextContent {
|
||||
gap: var(--bui-space-0_5);
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-TableCellProfile {
|
||||
gap: var(--bui-space-2);
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-TableCellProfileAvatar {
|
||||
vertical-align: middle;
|
||||
user-select: none;
|
||||
color: var(--bui-fg-primary);
|
||||
background-color: var(--bui-bg-surface-2);
|
||||
border-radius: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
display: inline-flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bui-TableCellProfileAvatarImage {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bui-TableCellProfileAvatarFallback {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: var(--bui-font-size-2);
|
||||
font-weight: var(--bui-font-weight-regular);
|
||||
box-shadow: inset 0 0 0 1px var(--bui-border);
|
||||
border-radius: var(--bui-radius-full);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-DataTablePagination {
|
||||
padding-top: var(--bui-space-5);
|
||||
justify-content: space-between;
|
||||
@@ -9732,6 +9851,19 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-Hidden {
|
||||
clip: rect(0 0 0 0) !important;
|
||||
clip-path: inset(100%) !important;
|
||||
white-space: nowrap !important;
|
||||
border: 0 !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
margin: -1px !important;
|
||||
padding: 0 !important;
|
||||
position: absolute !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.bui-Icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
@@ -10034,85 +10166,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.bui-TableRoot {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bui-TableHead {
|
||||
text-align: left;
|
||||
padding: var(--bui-space-3);
|
||||
font-size: var(--bui-font-size-3);
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-TableBody {
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-TableRow {
|
||||
border-bottom: 1px solid var(--bui-border);
|
||||
transition: color .2s ease-in-out;
|
||||
}
|
||||
|
||||
.bui-TableBody .bui-TableRow:hover {
|
||||
background-color: var(--bui-gray-2);
|
||||
}
|
||||
|
||||
.bui-TableCell {
|
||||
padding: var(--bui-space-3);
|
||||
font-size: var(--bui-font-size-3);
|
||||
}
|
||||
|
||||
.bui-TableCellText, .bui-TableCellLink {
|
||||
gap: var(--bui-space-0_5);
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-TableCellProfile {
|
||||
gap: var(--bui-space-2);
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-TableCellProfileAvatar {
|
||||
vertical-align: middle;
|
||||
user-select: none;
|
||||
color: var(--bui-fg-primary);
|
||||
background-color: var(--bui-bg-surface-2);
|
||||
border-radius: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
display: inline-flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bui-TableCellProfileAvatarImage {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bui-TableCellProfileAvatarFallback {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: var(--bui-font-size-2);
|
||||
font-weight: var(--bui-font-weight-regular);
|
||||
box-shadow: inset 0 0 0 1px var(--bui-border);
|
||||
border-radius: var(--bui-radius-full);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bui-Tabs {
|
||||
--active-tab-left: 0px;
|
||||
--active-tab-right: 0px;
|
||||
|
||||
+73
-117
@@ -11,7 +11,6 @@ import type { ComponentPropsWithRef } from 'react';
|
||||
import { Context } from 'react';
|
||||
import type { ElementType } from 'react';
|
||||
import { ForwardRefExoticComponent } from 'react';
|
||||
import { HTMLAttributes } from 'react';
|
||||
import { JSX as JSX_2 } from 'react/jsx-runtime';
|
||||
import { LinkProps as LinkProps_2 } from 'react-aria-components';
|
||||
import { Menu as Menu_2 } from '@base-ui-components/react/menu';
|
||||
@@ -30,12 +29,9 @@ import type { TabListProps as TabListProps_2 } from 'react-aria-components';
|
||||
import type { TabPanelProps as TabPanelProps_2 } from 'react-aria-components';
|
||||
import type { TabProps as TabProps_2 } from 'react-aria-components';
|
||||
import { TabsProps as TabsProps_2 } from 'react-aria-components';
|
||||
import { TdHTMLAttributes } from 'react';
|
||||
import type { TextFieldProps as TextFieldProps_2 } from 'react-aria-components';
|
||||
import { ThHTMLAttributes } from 'react';
|
||||
import { TooltipProps as TooltipProps_2 } from 'react-aria-components';
|
||||
import { TooltipTriggerComponentProps } from 'react-aria-components';
|
||||
import type { useRender } from '@base-ui-components/react/use-render';
|
||||
|
||||
// @public (undocumented)
|
||||
export type AlignItems = 'stretch' | 'start' | 'center' | 'end';
|
||||
@@ -519,7 +515,8 @@ export const componentDefinitions: {
|
||||
readonly caption: 'bui-TableCaption';
|
||||
readonly cell: 'bui-TableCell';
|
||||
readonly cellText: 'bui-TableCellText';
|
||||
readonly cellLink: 'bui-TableCellLink';
|
||||
readonly cellTextContent: 'bui-TableCellTextContent';
|
||||
readonly cellTextIcon: 'bui-TableCellTextIcon';
|
||||
readonly cellProfile: 'bui-TableCellProfile';
|
||||
readonly cellProfileAvatar: 'bui-TableCellProfileAvatar';
|
||||
readonly cellProfileAvatarImage: 'bui-TableCellProfileAvatarImage';
|
||||
@@ -609,68 +606,6 @@ export type DataAttributesMap = Record<string, DataAttributeValues>;
|
||||
// @public
|
||||
export type DataAttributeValues = readonly (string | number | boolean)[];
|
||||
|
||||
// @public
|
||||
export const DataTable: {
|
||||
Root: <TData>(
|
||||
props: {
|
||||
table: Table_2<TData>;
|
||||
} & React.HTMLAttributes<HTMLDivElement>,
|
||||
) => JSX.Element;
|
||||
Pagination: ForwardRefExoticComponent<
|
||||
DataTablePaginationProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
Table: ForwardRefExoticComponent<
|
||||
DataTableTableProps & RefAttributes<HTMLTableElement>
|
||||
>;
|
||||
TableRoot: ForwardRefExoticComponent<
|
||||
Omit<
|
||||
HTMLAttributes<HTMLTableElement> & RefAttributes<HTMLTableElement>,
|
||||
'ref'
|
||||
> &
|
||||
RefAttributes<HTMLTableElement>
|
||||
>;
|
||||
TableHeader: ForwardRefExoticComponent<
|
||||
HTMLAttributes<HTMLTableSectionElement> &
|
||||
RefAttributes<HTMLTableSectionElement>
|
||||
>;
|
||||
TableBody: ForwardRefExoticComponent<
|
||||
HTMLAttributes<HTMLTableSectionElement> &
|
||||
RefAttributes<HTMLTableSectionElement>
|
||||
>;
|
||||
TableRow: ForwardRefExoticComponent<
|
||||
HTMLAttributes<HTMLTableRowElement> & RefAttributes<HTMLTableRowElement>
|
||||
>;
|
||||
TableCell: ForwardRefExoticComponent<
|
||||
TdHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
|
||||
>;
|
||||
TableCellText: ForwardRefExoticComponent<
|
||||
TableCellTextProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
TableCellLink: ForwardRefExoticComponent<
|
||||
TableCellLinkProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
TableCellProfile: ForwardRefExoticComponent<
|
||||
TableCellProfileProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
TableHead: ForwardRefExoticComponent<
|
||||
ThHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
|
||||
>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface DataTablePaginationProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface DataTableRootProps<TData>
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
table: Table_2<TData>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface DataTableTableProps
|
||||
extends React.HTMLAttributes<HTMLTableElement> {}
|
||||
|
||||
// @public (undocumented)
|
||||
export type Display = 'none' | 'flex' | 'block' | 'inline';
|
||||
|
||||
@@ -1066,6 +1001,9 @@ export const heightPropDefs: {
|
||||
// @public (undocumented)
|
||||
export type HeightProps = GetPropDefTypes<typeof heightPropDefs>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const Hidden: (props: BoxProps) => JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export const Icon: (props: IconProps) => JSX_2.Element | null;
|
||||
|
||||
@@ -1567,78 +1505,96 @@ export interface SwitchProps extends SwitchProps_2 {
|
||||
// @public
|
||||
export const Tab: (props: TabProps) => JSX_2.Element;
|
||||
|
||||
// @public
|
||||
export const Table: {
|
||||
Root: ForwardRefExoticComponent<
|
||||
HTMLAttributes<HTMLTableElement> & RefAttributes<HTMLTableElement>
|
||||
>;
|
||||
Header: ForwardRefExoticComponent<
|
||||
HTMLAttributes<HTMLTableSectionElement> &
|
||||
RefAttributes<HTMLTableSectionElement>
|
||||
>;
|
||||
Body: ForwardRefExoticComponent<
|
||||
HTMLAttributes<HTMLTableSectionElement> &
|
||||
RefAttributes<HTMLTableSectionElement>
|
||||
>;
|
||||
Head: ForwardRefExoticComponent<
|
||||
ThHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
|
||||
>;
|
||||
Row: ForwardRefExoticComponent<
|
||||
HTMLAttributes<HTMLTableRowElement> & RefAttributes<HTMLTableRowElement>
|
||||
>;
|
||||
Cell: ForwardRefExoticComponent<
|
||||
TdHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
|
||||
>;
|
||||
CellText: ForwardRefExoticComponent<
|
||||
TableCellTextProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
CellLink: ForwardRefExoticComponent<
|
||||
TableCellLinkProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
CellProfile: ForwardRefExoticComponent<
|
||||
TableCellProfileProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
Caption: ForwardRefExoticComponent<
|
||||
HTMLAttributes<HTMLTableCaptionElement> &
|
||||
RefAttributes<HTMLTableCaptionElement>
|
||||
>;
|
||||
};
|
||||
// Warning: (ae-missing-release-tag) "Table" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export function Table<TData>(
|
||||
props: TableProps<TData> & {
|
||||
ref?: React.ForwardedRef<HTMLTableElement>;
|
||||
},
|
||||
): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TableCellLinkProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
// (undocumented)
|
||||
description?: string;
|
||||
// (undocumented)
|
||||
href: string;
|
||||
// (undocumented)
|
||||
render?: useRender.ComponentProps<'a'>['render'];
|
||||
// (undocumented)
|
||||
title: string;
|
||||
export namespace Table {
|
||||
var // (undocumented)
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const TableCellProfile: ForwardRefExoticComponent<
|
||||
TableCellProfileProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TableCellProfileProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
// (undocumented)
|
||||
color?: 'primary' | 'secondary';
|
||||
// (undocumented)
|
||||
description?: string;
|
||||
// (undocumented)
|
||||
href?: string;
|
||||
// (undocumented)
|
||||
name?: string;
|
||||
// (undocumented)
|
||||
src?: string;
|
||||
// (undocumented)
|
||||
to?: string;
|
||||
// (undocumented)
|
||||
withImage?: boolean;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const TableCellText: ForwardRefExoticComponent<
|
||||
TableCellTextProps & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TableCellTextProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
// (undocumented)
|
||||
color?: 'primary' | 'secondary';
|
||||
// (undocumented)
|
||||
description?: string;
|
||||
// (undocumented)
|
||||
href?: string;
|
||||
// (undocumented)
|
||||
leadingIcon?: React.ReactNode;
|
||||
// (undocumented)
|
||||
title: string;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "TablePagination" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export function TablePagination<TData>(
|
||||
props: TablePaginationProps<TData>,
|
||||
): JSX_2.Element;
|
||||
|
||||
// @public (undocumented)
|
||||
export namespace TablePagination {
|
||||
var // (undocumented)
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TablePaginationProps<TData>
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
// (undocumented)
|
||||
onNextPage?: () => void;
|
||||
// (undocumented)
|
||||
onPageSizeChange?: (pageSize: number) => void;
|
||||
// (undocumented)
|
||||
onPreviousPage?: () => void;
|
||||
// (undocumented)
|
||||
showPageSizeOptions?: boolean;
|
||||
// (undocumented)
|
||||
table?: Table_2<TData>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface TableProps<TData>
|
||||
extends React.HTMLAttributes<HTMLTableElement> {
|
||||
// (undocumented)
|
||||
table: Table_2<TData>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const TabList: (props: TabListProps) => JSX_2.Element;
|
||||
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 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 type { Meta, StoryFn, StoryObj } from '@storybook/react';
|
||||
import { DataTable } from '.';
|
||||
import { DataTablePagination } from '../DataTablePagination';
|
||||
import {
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableHead,
|
||||
Table,
|
||||
} from '../Table';
|
||||
import { data, DataProps } from './mocked-components';
|
||||
import { columns } from './mocked-columns';
|
||||
import {
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
useReactTable,
|
||||
PaginationState,
|
||||
} from '@tanstack/react-table';
|
||||
import { useState } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/DataTable',
|
||||
decorators: [
|
||||
(Story: StoryFn) => (
|
||||
<MemoryRouter>
|
||||
<Story />
|
||||
</MemoryRouter>
|
||||
),
|
||||
],
|
||||
} satisfies Meta;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Uncontrolled: Story = {
|
||||
render: () => {
|
||||
const table = useReactTable<DataProps>({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<DataTable table={table} />
|
||||
<DataTablePagination table={table} />
|
||||
</>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const Controlled: Story = {
|
||||
render: () => {
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 4,
|
||||
pageSize: 5,
|
||||
});
|
||||
|
||||
const table = useReactTable<DataProps>({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
state: {
|
||||
pagination,
|
||||
},
|
||||
onPaginationChange: setPagination,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<DataTable table={table} />
|
||||
<DataTablePagination table={table} />
|
||||
</>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCustomTable: Story = {
|
||||
render: () => {
|
||||
const table = useReactTable<DataProps>({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map(header => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map(row => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && 'selected'}
|
||||
>
|
||||
{row.getVisibleCells().map(cell => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<DataTablePagination table={table} />
|
||||
</>
|
||||
);
|
||||
},
|
||||
};
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 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 { DataTableProps } from './types';
|
||||
import {
|
||||
Table,
|
||||
TableRow,
|
||||
TableHeader,
|
||||
TableHead,
|
||||
TableBody,
|
||||
TableCell,
|
||||
} from '../Table';
|
||||
import { flexRender } from '@tanstack/react-table';
|
||||
import { DataTableHeadContent } from './DataTableHeadContent';
|
||||
|
||||
function getAriaSort(sortDirection: string | false) {
|
||||
if (sortDirection === 'asc') {
|
||||
return 'ascending';
|
||||
}
|
||||
if (sortDirection === 'desc') {
|
||||
return 'descending';
|
||||
}
|
||||
return 'none';
|
||||
}
|
||||
|
||||
/** @public */
|
||||
function DataTable<TData>(
|
||||
props: DataTableProps<TData> & { ref?: React.ForwardedRef<HTMLTableElement> },
|
||||
) {
|
||||
const { className, table, ref, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Table
|
||||
ref={ref}
|
||||
style={{ minWidth: table.getTotalSize() }}
|
||||
className={clsx(className)}
|
||||
{...rest}
|
||||
>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map(header => {
|
||||
return (
|
||||
<TableHead
|
||||
key={header.id}
|
||||
style={{ width: header.getSize() }}
|
||||
aria-sort={getAriaSort(header.column.getIsSorted())}
|
||||
>
|
||||
{header.isPlaceholder ? null : (
|
||||
<DataTableHeadContent header={header} />
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map(row => {
|
||||
const rowData = row.original as TData & { onClick?: () => void };
|
||||
const handleRowClick = rowData.onClick
|
||||
? (e: React.MouseEvent<HTMLTableRowElement>) => {
|
||||
if (!e.isPropagationStopped()) {
|
||||
rowData.onClick!();
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && 'selected'}
|
||||
data-clickable={!!rowData.onClick}
|
||||
onClick={handleRowClick}
|
||||
>
|
||||
{row.getVisibleCells().map(cell => (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
style={{ width: cell.column.getSize() }}
|
||||
>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={table.getAllColumns().length}
|
||||
className="h-24 text-center"
|
||||
style={{ width: table.getTotalSize() }}
|
||||
>
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
DataTable.displayName = 'DataTable';
|
||||
|
||||
export { DataTable };
|
||||
@@ -17,6 +17,7 @@
|
||||
import { Box, BoxProps } from '../Box';
|
||||
import clsx from 'clsx';
|
||||
|
||||
/** @public */
|
||||
export const Hidden = (props: BoxProps) => {
|
||||
return <Box {...props} className={clsx('bui-Hidden', props.className)} />;
|
||||
};
|
||||
|
||||
+6
-10
@@ -13,12 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { flexRender } from '@tanstack/react-table';
|
||||
import { Icon } from '../Icon';
|
||||
import { Header } from '@tanstack/react-table';
|
||||
import { Hidden } from '../Hidden';
|
||||
import { Icon } from '../../Icon';
|
||||
import { Hidden } from '../../Hidden';
|
||||
import { RawHeadContentProps } from './types';
|
||||
|
||||
function getSortTitle(nextOrder: string | false) {
|
||||
if (nextOrder === 'asc') {
|
||||
@@ -30,13 +31,8 @@ function getSortTitle(nextOrder: string | false) {
|
||||
return 'Clear sort';
|
||||
}
|
||||
|
||||
interface DataTableHeadContentProps<TData> {
|
||||
header: Header<TData, unknown>;
|
||||
}
|
||||
|
||||
export function DataTableHeadContent<TData>({
|
||||
header,
|
||||
}: DataTableHeadContentProps<TData>) {
|
||||
/** @internal */
|
||||
export function RawHeadContent<TData>({ header }: RawHeadContentProps<TData>) {
|
||||
const headerContent = useMemo(
|
||||
() => flexRender(header.column.columnDef.header, header.getContext()),
|
||||
[header],
|
||||
+2
-2
@@ -14,5 +14,5 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { DataTable } from './DataTable';
|
||||
export type { DataTableProps } from './types';
|
||||
export { RawHeadContent } from './RawHeadContent';
|
||||
export type { RawHeadContentProps } from './types';
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2025 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 { Header } from '@tanstack/react-table';
|
||||
|
||||
/** @internal */
|
||||
export interface RawHeadContentProps<TData> {
|
||||
header: Header<TData, unknown>;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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 { ComponentType } from 'react';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import {
|
||||
RawTable,
|
||||
RawTableBody,
|
||||
RawTableHead,
|
||||
RawTableHeader,
|
||||
RawTableRow,
|
||||
RawTableCell,
|
||||
} from '.';
|
||||
|
||||
const invoices = [
|
||||
{
|
||||
invoice: 'INV001',
|
||||
paymentStatus: 'Paid',
|
||||
totalAmount: '$250.00',
|
||||
paymentMethod: 'Credit Card',
|
||||
},
|
||||
{
|
||||
invoice: 'INV002',
|
||||
paymentStatus: 'Pending',
|
||||
totalAmount: '$150.00',
|
||||
paymentMethod: 'PayPal',
|
||||
},
|
||||
{
|
||||
invoice: 'INV003',
|
||||
paymentStatus: 'Unpaid',
|
||||
totalAmount: '$350.00',
|
||||
paymentMethod: 'Bank Transfer',
|
||||
},
|
||||
{
|
||||
invoice: 'INV004',
|
||||
paymentStatus: 'Paid',
|
||||
totalAmount: '$450.00',
|
||||
paymentMethod: 'Credit Card',
|
||||
},
|
||||
{
|
||||
invoice: 'INV005',
|
||||
paymentStatus: 'Paid',
|
||||
totalAmount: '$550.00',
|
||||
paymentMethod: 'PayPal',
|
||||
},
|
||||
{
|
||||
invoice: 'INV006',
|
||||
paymentStatus: 'Pending',
|
||||
totalAmount: '$200.00',
|
||||
paymentMethod: 'Bank Transfer',
|
||||
},
|
||||
{
|
||||
invoice: 'INV007',
|
||||
paymentStatus: 'Unpaid',
|
||||
totalAmount: '$300.00',
|
||||
paymentMethod: 'Credit Card',
|
||||
},
|
||||
];
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Table/RawTable',
|
||||
component: RawTable,
|
||||
subcomponents: {
|
||||
Body: RawTableBody as ComponentType<unknown>,
|
||||
Cell: RawTableCell as ComponentType<unknown>,
|
||||
Head: RawTableHead as ComponentType<unknown>,
|
||||
Header: RawTableHeader as ComponentType<unknown>,
|
||||
Row: RawTableRow as ComponentType<unknown>,
|
||||
},
|
||||
} satisfies Meta<typeof RawTable>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => (
|
||||
<RawTable>
|
||||
<RawTableHeader>
|
||||
<RawTableRow>
|
||||
<RawTableHead className="w-[100px]">Invoice</RawTableHead>
|
||||
<RawTableHead>Status</RawTableHead>
|
||||
<RawTableHead>Method</RawTableHead>
|
||||
<RawTableHead className="text-right">Amount</RawTableHead>
|
||||
</RawTableRow>
|
||||
</RawTableHeader>
|
||||
<RawTableBody>
|
||||
{invoices.map(invoice => (
|
||||
<RawTableRow key={invoice.invoice}>
|
||||
<RawTableCell className="font-medium">
|
||||
{invoice.invoice}
|
||||
</RawTableCell>
|
||||
<RawTableCell>{invoice.paymentStatus}</RawTableCell>
|
||||
<RawTableCell>{invoice.paymentMethod}</RawTableCell>
|
||||
<RawTableCell className="text-right">
|
||||
{invoice.totalAmount}
|
||||
</RawTableCell>
|
||||
</RawTableRow>
|
||||
))}
|
||||
</RawTableBody>
|
||||
</RawTable>
|
||||
),
|
||||
};
|
||||
+27
-15
@@ -14,21 +14,33 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useStyles } from '../../../hooks/useStyles';
|
||||
.bui-TableRoot {
|
||||
width: 100%;
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
const TableCell = forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
.bui-TableHead {
|
||||
text-align: left;
|
||||
padding: var(--bui-space-3);
|
||||
font-size: var(--bui-font-size-3);
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
return (
|
||||
<td ref={ref} className={clsx(classNames.cell, className)} {...props} />
|
||||
);
|
||||
});
|
||||
TableCell.displayName = 'TableCell';
|
||||
.bui-TableBody {
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
export { TableCell };
|
||||
.bui-TableRow {
|
||||
border-bottom: 1px solid var(--bui-border);
|
||||
transition: color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.bui-TableBody .bui-TableRow:hover {
|
||||
background-color: var(--bui-gray-2);
|
||||
}
|
||||
|
||||
.bui-TableCell {
|
||||
padding: var(--bui-space-3);
|
||||
font-size: var(--bui-font-size-3);
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2025 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 { forwardRef } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useStyles } from '../../../hooks/useStyles';
|
||||
|
||||
/** @internal */
|
||||
export const RawTable = forwardRef<
|
||||
HTMLTableElement,
|
||||
React.HTMLAttributes<HTMLTableElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<table ref={ref} className={clsx(classNames.root, className)} {...props} />
|
||||
);
|
||||
});
|
||||
RawTable.displayName = 'RawTable';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableHeader = forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<thead
|
||||
ref={ref}
|
||||
className={clsx(classNames.header, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
RawTableHeader.displayName = 'RawTableHeader';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableBody = forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<tbody ref={ref} className={clsx(classNames.body, className)} {...props} />
|
||||
);
|
||||
});
|
||||
RawTableBody.displayName = 'RawTableBody';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableRow = forwardRef<
|
||||
HTMLTableRowElement,
|
||||
React.HTMLAttributes<HTMLTableRowElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<tr ref={ref} className={clsx(classNames.row, className)} {...props}>
|
||||
{props.children}
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
RawTableRow.displayName = 'RawTableRow';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableHead = forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.ThHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<th ref={ref} className={clsx(classNames.head, className)} {...props} />
|
||||
);
|
||||
});
|
||||
RawTableHead.displayName = 'RawTableHead';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableCaption = forwardRef<
|
||||
HTMLTableCaptionElement,
|
||||
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<caption
|
||||
ref={ref}
|
||||
className={clsx(classNames.caption, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
RawTableCaption.displayName = 'RawTableCaption';
|
||||
|
||||
/** @internal */
|
||||
export const RawTableCell = forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<td ref={ref} className={clsx(classNames.cell, className)} {...props} />
|
||||
);
|
||||
});
|
||||
RawTableCell.displayName = 'RawTableCell';
|
||||
+9
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
* Copyright 2025 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.
|
||||
@@ -14,7 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.bui-TableCell {
|
||||
padding: var(--bui-space-3);
|
||||
font-size: var(--bui-font-size-3);
|
||||
}
|
||||
export {
|
||||
RawTable,
|
||||
RawTableBody,
|
||||
RawTableHead,
|
||||
RawTableHeader,
|
||||
RawTableRow,
|
||||
RawTableCell,
|
||||
} from './RawTable';
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
* Copyright 2025 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.
|
||||
@@ -14,98 +14,74 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentType } from 'react';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import type { Meta, StoryFn, StoryObj } from '@storybook/react';
|
||||
import { Table, TablePagination } from '.';
|
||||
import { data, DataProps } from './mocked-components';
|
||||
import { columns } from './mocked-columns';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '.';
|
||||
|
||||
const invoices = [
|
||||
{
|
||||
invoice: 'INV001',
|
||||
paymentStatus: 'Paid',
|
||||
totalAmount: '$250.00',
|
||||
paymentMethod: 'Credit Card',
|
||||
},
|
||||
{
|
||||
invoice: 'INV002',
|
||||
paymentStatus: 'Pending',
|
||||
totalAmount: '$150.00',
|
||||
paymentMethod: 'PayPal',
|
||||
},
|
||||
{
|
||||
invoice: 'INV003',
|
||||
paymentStatus: 'Unpaid',
|
||||
totalAmount: '$350.00',
|
||||
paymentMethod: 'Bank Transfer',
|
||||
},
|
||||
{
|
||||
invoice: 'INV004',
|
||||
paymentStatus: 'Paid',
|
||||
totalAmount: '$450.00',
|
||||
paymentMethod: 'Credit Card',
|
||||
},
|
||||
{
|
||||
invoice: 'INV005',
|
||||
paymentStatus: 'Paid',
|
||||
totalAmount: '$550.00',
|
||||
paymentMethod: 'PayPal',
|
||||
},
|
||||
{
|
||||
invoice: 'INV006',
|
||||
paymentStatus: 'Pending',
|
||||
totalAmount: '$200.00',
|
||||
paymentMethod: 'Bank Transfer',
|
||||
},
|
||||
{
|
||||
invoice: 'INV007',
|
||||
paymentStatus: 'Unpaid',
|
||||
totalAmount: '$300.00',
|
||||
paymentMethod: 'Credit Card',
|
||||
},
|
||||
];
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
useReactTable,
|
||||
PaginationState,
|
||||
} from '@tanstack/react-table';
|
||||
import { useState } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Table',
|
||||
component: Table,
|
||||
subcomponents: {
|
||||
Body: TableBody as ComponentType<unknown>,
|
||||
Cell: TableCell as ComponentType<unknown>,
|
||||
Head: TableHead as ComponentType<unknown>,
|
||||
Header: TableHeader as ComponentType<unknown>,
|
||||
Row: TableRow as ComponentType<unknown>,
|
||||
},
|
||||
} satisfies Meta<typeof Table>;
|
||||
decorators: [
|
||||
(Story: StoryFn) => (
|
||||
<MemoryRouter>
|
||||
<Story />
|
||||
</MemoryRouter>
|
||||
),
|
||||
],
|
||||
} satisfies Meta;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[100px]">Invoice</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Method</TableHead>
|
||||
<TableHead className="text-right">Amount</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{invoices.map(invoice => (
|
||||
<TableRow key={invoice.invoice}>
|
||||
<TableCell className="font-medium">{invoice.invoice}</TableCell>
|
||||
<TableCell>{invoice.paymentStatus}</TableCell>
|
||||
<TableCell>{invoice.paymentMethod}</TableCell>
|
||||
<TableCell className="text-right">{invoice.totalAmount}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
),
|
||||
export const Uncontrolled: Story = {
|
||||
render: () => {
|
||||
const table = useReactTable<DataProps>({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table table={table} />
|
||||
<TablePagination table={table} />
|
||||
</>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const Controlled: Story = {
|
||||
render: () => {
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 4,
|
||||
pageSize: 5,
|
||||
});
|
||||
|
||||
const table = useReactTable<DataProps>({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
state: {
|
||||
pagination,
|
||||
},
|
||||
onPaginationChange: setPagination,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table table={table} />
|
||||
<TablePagination table={table} />
|
||||
</>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -14,94 +14,107 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useStyles } from '../../hooks/useStyles';
|
||||
import { TableProps } from './types';
|
||||
import {
|
||||
RawTable,
|
||||
RawTableRow,
|
||||
RawTableHeader,
|
||||
RawTableHead,
|
||||
RawTableBody,
|
||||
RawTableCell,
|
||||
} from './RawTable';
|
||||
import { RawHeadContent } from './RawHeadContent';
|
||||
import { flexRender } from '@tanstack/react-table';
|
||||
|
||||
function getAriaSort(sortDirection: string | false) {
|
||||
if (sortDirection === 'asc') {
|
||||
return 'ascending';
|
||||
}
|
||||
if (sortDirection === 'desc') {
|
||||
return 'descending';
|
||||
}
|
||||
return 'none';
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export const Table = forwardRef<
|
||||
HTMLTableElement,
|
||||
React.HTMLAttributes<HTMLTableElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
function Table<TData>(
|
||||
props: TableProps<TData> & { ref?: React.ForwardedRef<HTMLTableElement> },
|
||||
) {
|
||||
const { className, table, ref, ...rest } = props;
|
||||
|
||||
return (
|
||||
<table ref={ref} className={clsx(classNames.root, className)} {...props} />
|
||||
<RawTable
|
||||
ref={ref}
|
||||
style={{ minWidth: table.getTotalSize() }}
|
||||
className={clsx(className)}
|
||||
{...rest}
|
||||
>
|
||||
<RawTableHeader>
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
<RawTableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map(header => {
|
||||
return (
|
||||
<RawTableHead
|
||||
key={header.id}
|
||||
style={{ width: header.getSize() }}
|
||||
aria-sort={getAriaSort(header.column.getIsSorted())}
|
||||
>
|
||||
{header.isPlaceholder ? null : (
|
||||
<RawHeadContent header={header} />
|
||||
)}
|
||||
</RawTableHead>
|
||||
);
|
||||
})}
|
||||
</RawTableRow>
|
||||
))}
|
||||
</RawTableHeader>
|
||||
<RawTableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map(row => {
|
||||
const rowData = row.original as TData & { onClick?: () => void };
|
||||
const handleRowClick = rowData.onClick
|
||||
? (e: React.MouseEvent<HTMLTableRowElement>) => {
|
||||
if (!e.isPropagationStopped()) {
|
||||
rowData.onClick!();
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<RawTableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && 'selected'}
|
||||
data-clickable={!!rowData.onClick}
|
||||
onClick={handleRowClick}
|
||||
>
|
||||
{row.getVisibleCells().map(cell => (
|
||||
<RawTableCell
|
||||
key={cell.id}
|
||||
style={{ width: cell.column.getSize() }}
|
||||
>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</RawTableCell>
|
||||
))}
|
||||
</RawTableRow>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<RawTableRow>
|
||||
<RawTableCell
|
||||
colSpan={table.getAllColumns().length}
|
||||
className="h-24 text-center"
|
||||
style={{ width: table.getTotalSize() }}
|
||||
>
|
||||
No results.
|
||||
</RawTableCell>
|
||||
</RawTableRow>
|
||||
)}
|
||||
</RawTableBody>
|
||||
</RawTable>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Table.displayName = 'Table';
|
||||
|
||||
/** @public */
|
||||
export const TableHeader = forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<thead
|
||||
ref={ref}
|
||||
className={clsx(classNames.header, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
TableHeader.displayName = 'TableHeader';
|
||||
|
||||
/** @public */
|
||||
export const TableBody = forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<tbody ref={ref} className={clsx(classNames.body, className)} {...props} />
|
||||
);
|
||||
});
|
||||
TableBody.displayName = 'TableBody';
|
||||
|
||||
/** @public */
|
||||
export const TableRow = forwardRef<
|
||||
HTMLTableRowElement,
|
||||
React.HTMLAttributes<HTMLTableRowElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<tr ref={ref} className={clsx(classNames.row, className)} {...props}>
|
||||
{props.children}
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
TableRow.displayName = 'TableRow';
|
||||
|
||||
/** @public */
|
||||
export const TableHead = forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.ThHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<th ref={ref} className={clsx(classNames.head, className)} {...props} />
|
||||
);
|
||||
});
|
||||
TableHead.displayName = 'TableHead';
|
||||
|
||||
/** @public */
|
||||
export const TableCaption = forwardRef<
|
||||
HTMLTableCaptionElement,
|
||||
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { classNames } = useStyles('Table');
|
||||
|
||||
return (
|
||||
<caption
|
||||
ref={ref}
|
||||
className={clsx(classNames.caption, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
TableCaption.displayName = 'TableCaption';
|
||||
export { Table };
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* 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 type { Meta, StoryObj } from '@storybook/react';
|
||||
import { TableCell } from './TableCell';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Table/TableCell',
|
||||
component: TableCell,
|
||||
} satisfies Meta<typeof TableCell>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: 'Hello world',
|
||||
},
|
||||
};
|
||||
+7
-7
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { DataTablePagination } from './DataTablePagination';
|
||||
import { TablePagination } from './TablePagination';
|
||||
import {
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
@@ -23,13 +23,13 @@ import {
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import { data, DataProps } from '../DataTable/mocked-components';
|
||||
import { columns } from '../DataTable/mocked-columns';
|
||||
import { data, DataProps } from '../mocked-components';
|
||||
import { columns } from '../mocked-columns';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/DataTablePagination',
|
||||
component: DataTablePagination,
|
||||
} satisfies Meta<typeof DataTablePagination>;
|
||||
title: 'Components/Table/TablePagination',
|
||||
component: TablePagination,
|
||||
} satisfies Meta<typeof TablePagination>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
@@ -45,6 +45,6 @@ export const Default: Story = {
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
});
|
||||
|
||||
return <DataTablePagination table={table} />;
|
||||
return <TablePagination table={table} />;
|
||||
},
|
||||
};
|
||||
+8
-8
@@ -14,15 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Text } from '../Text';
|
||||
import { DataTablePaginationProps } from './types';
|
||||
import { ButtonIcon } from '../ButtonIcon';
|
||||
import { Text } from '../../Text';
|
||||
import { TablePaginationProps } from './types';
|
||||
import { ButtonIcon } from '../../ButtonIcon';
|
||||
import clsx from 'clsx';
|
||||
import { Select } from '../Select';
|
||||
import { Icon } from '../Icon';
|
||||
import { Select } from '../../Select';
|
||||
import { Icon } from '../../Icon';
|
||||
|
||||
/** @public */
|
||||
function DataTablePagination<TData>(props: DataTablePaginationProps<TData>) {
|
||||
function TablePagination<TData>(props: TablePaginationProps<TData>) {
|
||||
const {
|
||||
className,
|
||||
table,
|
||||
@@ -103,6 +103,6 @@ function DataTablePagination<TData>(props: DataTablePaginationProps<TData>) {
|
||||
);
|
||||
}
|
||||
|
||||
DataTablePagination.displayName = 'DataTablePagination';
|
||||
TablePagination.displayName = 'TablePagination';
|
||||
|
||||
export { DataTablePagination };
|
||||
export { TablePagination };
|
||||
+2
-2
@@ -14,5 +14,5 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { DataTablePagination } from './DataTablePagination';
|
||||
export type { DataTablePaginationProps } from './types';
|
||||
export { TablePagination } from './TablePagination';
|
||||
export type { TablePaginationProps } from './types';
|
||||
+1
-8
@@ -17,7 +17,7 @@
|
||||
import { Table } from '@tanstack/react-table';
|
||||
|
||||
/** @public */
|
||||
export interface DataTablePaginationProps<TData>
|
||||
export interface TablePaginationProps<TData>
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
table?: Table<TData>;
|
||||
onNextPage?: () => void;
|
||||
@@ -25,10 +25,3 @@ export interface DataTablePaginationProps<TData>
|
||||
onPageSizeChange?: (pageSize: number) => void;
|
||||
showPageSizeOptions?: boolean;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export type DataTablePaginationComponent = <TData>(
|
||||
props: DataTablePaginationProps<TData> & {
|
||||
ref?: React.ForwardedRef<HTMLDivElement>;
|
||||
},
|
||||
) => React.ReactElement;
|
||||
@@ -14,9 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { Table, TableBody, TableHead, TableHeader, TableRow } from './Table';
|
||||
export { TableCell } from './TableCell/TableCell';
|
||||
export { Table } from './Table';
|
||||
export { TableCellText } from './TableCellText/TableCellText';
|
||||
export { TableCellProfile } from './TableCellProfile/TableCellProfile';
|
||||
export { TablePagination } from './TablePagination';
|
||||
|
||||
export type { TableProps } from './types';
|
||||
export type { TableCellTextProps } from './TableCellText/types';
|
||||
export type { TableCellProfileProps } from './TableCellProfile/types';
|
||||
export type { TablePaginationProps } from './TablePagination/types';
|
||||
|
||||
+2
-2
@@ -17,8 +17,8 @@
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import { DataProps } from './mocked-components';
|
||||
import { Checkbox } from '../Checkbox';
|
||||
import { TableCellText } from '../Table/TableCellText/TableCellText';
|
||||
import { TableCellProfile } from '../Table/TableCellProfile/TableCellProfile';
|
||||
import { TableCellText } from './TableCellText/TableCellText';
|
||||
import { TableCellProfile } from './TableCellProfile/TableCellProfile';
|
||||
|
||||
export const columns: ColumnDef<DataProps>[] = [
|
||||
{
|
||||
@@ -1,25 +0,0 @@
|
||||
.bui-TableRoot {
|
||||
width: 100%;
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.bui-TableHead {
|
||||
text-align: left;
|
||||
padding: var(--bui-space-3);
|
||||
font-size: var(--bui-font-size-3);
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-TableBody {
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-TableRow {
|
||||
border-bottom: 1px solid var(--bui-border);
|
||||
transition: color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.bui-TableBody .bui-TableRow:hover {
|
||||
background-color: var(--bui-gray-2);
|
||||
}
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
import { Table } from '@tanstack/react-table';
|
||||
|
||||
/** @public */
|
||||
export interface DataTableProps<TData>
|
||||
export interface TableProps<TData>
|
||||
extends React.HTMLAttributes<HTMLTableElement> {
|
||||
table: Table<TData>;
|
||||
}
|
||||
@@ -22,8 +22,13 @@
|
||||
@import '../components/Checkbox/styles.css';
|
||||
@import '../components/Collapsible/Collapsible.styles.css';
|
||||
@import '../components/Container/styles.css';
|
||||
@import '../components/DataTable/DataTable.styles.css';
|
||||
@import '../components/DataTablePagination/DataTablePagination.styles.css';
|
||||
|
||||
@import '../components/Table/Table.styles.css';
|
||||
@import '../components/Table/RawTable/RawTable.styles.css';
|
||||
@import '../components/Table/TableCellText/TableCellText.styles.css';
|
||||
@import '../components/Table/TableCellProfile/TableCellProfile.styles.css';
|
||||
@import '../components/Table/TablePagination/TablePagination.styles.css';
|
||||
|
||||
@import '../components/FieldError/FieldError.styles.css';
|
||||
@import '../components/FieldLabel/FieldLabel.styles.css';
|
||||
@import '../components/Flex/styles.css';
|
||||
@@ -36,10 +41,6 @@
|
||||
@import '../components/Menu/Menu.styles.css';
|
||||
@import '../components/Popover/Popover.styles.css';
|
||||
@import '../components/RadioGroup/RadioGroup.styles.css';
|
||||
@import '../components/Table/styles.css';
|
||||
@import '../components/Table/TableCell/TableCell.styles.css';
|
||||
@import '../components/Table/TableCellText/TableCellText.styles.css';
|
||||
@import '../components/Table/TableCellProfile/TableCellProfile.styles.css';
|
||||
@import '../components/Tabs/Tabs.styles.css';
|
||||
@import '../components/Text/styles.css';
|
||||
@import '../components/TextField/TextField.styles.css';
|
||||
|
||||
@@ -34,8 +34,6 @@ export * from './components/Avatar';
|
||||
export * from './components/Button';
|
||||
export * from './components/Card';
|
||||
export * from './components/Collapsible';
|
||||
export * from './components/DataTable';
|
||||
export * from './components/DataTablePagination';
|
||||
export * from './components/FieldLabel';
|
||||
export * from './components/Header';
|
||||
export * from './components/HeaderPage';
|
||||
|
||||
Reference in New Issue
Block a user