Merge branch 'master' into canon-breakpoint-helpers

This commit is contained in:
Charles de Dreuille
2025-04-14 11:53:32 +02:00
39 changed files with 2178 additions and 332 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/canon': patch
---
Add new DataTable component and update Table component styles.
+52 -44
View File
@@ -94,6 +94,36 @@
height: 1.5rem;
}
.canon-DataTableRoot {
gap: var(--canon-space-3);
flex-direction: column;
display: flex;
}
.canon-DataTablePagination {
padding-top: var(--canon-space-5);
justify-content: space-between;
align-items: center;
display: flex;
}
.canon-DataTablePagination--left {
justify-content: space-between;
align-items: center;
display: flex;
}
.canon-DataTablePagination--right {
justify-content: space-between;
align-items: center;
gap: var(--canon-space-2);
display: flex;
}
.canon-DataTablePagination--select {
min-width: 10.5rem;
}
.canon-Flex {
display: flex;
}
@@ -179,63 +209,41 @@
display: flex;
}
.table {
background-color: var(--canon-bg-surface-1);
border-radius: var(--canon-radius-2);
.canon-TableRoot {
caption-side: bottom;
border-collapse: collapse;
width: 100%;
padding-bottom: var(--canon-space-0_5);
padding-top: var(--canon-space-0_5);
font-size: var(--canon-font-size-3);
font-family: var(--canon-font-regular);
font-weight: var(--canon-font-weight-regular);
position: relative;
overflow: auto;
& table {
caption-side: bottom;
width: 100%;
font-size: var(--canon-font-size-sm);
border-collapse: collapse;
}
}
.table-head {
.canon-TableHead {
text-align: left;
padding: var(--canon-space-3);
font-size: var(--canon-font-size-3);
color: var(--canon-fg-primary);
}
.table-body {
& tr:last-child {
border-bottom: none;
}
.canon-TableBody {
color: var(--canon-fg-primary);
}
.table-row {
.canon-TableRow {
border-bottom: 1px solid var(--canon-border);
transition: color .2s ease-in-out;
&:hover td {
background-color: var(--canon-bg-tint);
}
& .table-cell:first-child {
border-top-left-radius: var(--canon-radius-2);
border-bottom-left-radius: var(--canon-radius-2);
box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1), inset 4px -2px 0 0 var(--canon-bg-surface-1);
padding-left: var(--canon-space-3);
}
& .table-cell:last-child {
border-top-right-radius: var(--canon-radius-2);
border-bottom-right-radius: var(--canon-radius-2);
box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1), inset -4px -2px 0 0 var(--canon-bg-surface-1);
padding-right: var(--canon-space-3);
}
}
.table-cell {
.canon-TableBody .canon-TableRow:hover {
background-color: var(--canon-gray-2);
}
.canon-TableCell {
padding: var(--canon-space-3);
background-color: var(--canon-bg);
box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1), inset 0px -2px 0 0 var(--canon-bg-surface-1);
font-size: var(--canon-font-size-3);
}
.canon-TableCellText, .canon-TableCellLink {
gap: var(--canon-space-0_5);
flex-direction: column;
display: flex;
}
.canon-Text {
+23
View File
@@ -0,0 +1,23 @@
.canon-DataTablePagination {
padding-top: var(--canon-space-5);
justify-content: space-between;
align-items: center;
display: flex;
}
.canon-DataTablePagination--left {
justify-content: space-between;
align-items: center;
display: flex;
}
.canon-DataTablePagination--right {
justify-content: space-between;
align-items: center;
gap: var(--canon-space-2);
display: flex;
}
.canon-DataTablePagination--select {
min-width: 10.5rem;
}
+52 -44
View File
@@ -9318,6 +9318,36 @@
height: 1.5rem;
}
.canon-DataTableRoot {
gap: var(--canon-space-3);
flex-direction: column;
display: flex;
}
.canon-DataTablePagination {
padding-top: var(--canon-space-5);
justify-content: space-between;
align-items: center;
display: flex;
}
.canon-DataTablePagination--left {
justify-content: space-between;
align-items: center;
display: flex;
}
.canon-DataTablePagination--right {
justify-content: space-between;
align-items: center;
gap: var(--canon-space-2);
display: flex;
}
.canon-DataTablePagination--select {
min-width: 10.5rem;
}
.canon-Flex {
display: flex;
}
@@ -9403,63 +9433,41 @@
display: flex;
}
.table {
background-color: var(--canon-bg-surface-1);
border-radius: var(--canon-radius-2);
.canon-TableRoot {
caption-side: bottom;
border-collapse: collapse;
width: 100%;
padding-bottom: var(--canon-space-0_5);
padding-top: var(--canon-space-0_5);
font-size: var(--canon-font-size-3);
font-family: var(--canon-font-regular);
font-weight: var(--canon-font-weight-regular);
position: relative;
overflow: auto;
& table {
caption-side: bottom;
width: 100%;
font-size: var(--canon-font-size-sm);
border-collapse: collapse;
}
}
.table-head {
.canon-TableHead {
text-align: left;
padding: var(--canon-space-3);
font-size: var(--canon-font-size-3);
color: var(--canon-fg-primary);
}
.table-body {
& tr:last-child {
border-bottom: none;
}
.canon-TableBody {
color: var(--canon-fg-primary);
}
.table-row {
.canon-TableRow {
border-bottom: 1px solid var(--canon-border);
transition: color .2s ease-in-out;
&:hover td {
background-color: var(--canon-bg-tint);
}
& .table-cell:first-child {
border-top-left-radius: var(--canon-radius-2);
border-bottom-left-radius: var(--canon-radius-2);
box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1), inset 4px -2px 0 0 var(--canon-bg-surface-1);
padding-left: var(--canon-space-3);
}
& .table-cell:last-child {
border-top-right-radius: var(--canon-radius-2);
border-bottom-right-radius: var(--canon-radius-2);
box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1), inset -4px -2px 0 0 var(--canon-bg-surface-1);
padding-right: var(--canon-space-3);
}
}
.table-cell {
.canon-TableBody .canon-TableRow:hover {
background-color: var(--canon-gray-2);
}
.canon-TableCell {
padding: var(--canon-space-3);
background-color: var(--canon-bg);
box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1), inset 0px -2px 0 0 var(--canon-bg-surface-1);
font-size: var(--canon-font-size-3);
}
.canon-TableCellText, .canon-TableCellLink {
gap: var(--canon-space-0_5);
flex-direction: column;
display: flex;
}
.canon-Text {
+2 -56
View File
@@ -1,58 +1,4 @@
.table {
background-color: var(--canon-bg-surface-1);
border-radius: var(--canon-radius-2);
width: 100%;
padding-bottom: var(--canon-space-0_5);
padding-top: var(--canon-space-0_5);
.canon-TableCell {
padding: var(--canon-space-3);
font-size: var(--canon-font-size-3);
font-family: var(--canon-font-regular);
font-weight: var(--canon-font-weight-regular);
position: relative;
overflow: auto;
& table {
caption-side: bottom;
width: 100%;
font-size: var(--canon-font-size-sm);
border-collapse: collapse;
}
}
.table-head {
text-align: left;
padding: var(--canon-space-3);
}
.table-body {
& tr:last-child {
border-bottom: none;
}
}
.table-row {
transition: color .2s ease-in-out;
&:hover td {
background-color: var(--canon-bg-tint);
}
& .table-cell:first-child {
border-top-left-radius: var(--canon-radius-2);
border-bottom-left-radius: var(--canon-radius-2);
box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1), inset 4px -2px 0 0 var(--canon-bg-surface-1);
padding-left: var(--canon-space-3);
}
& .table-cell:last-child {
border-top-right-radius: var(--canon-radius-2);
border-bottom-right-radius: var(--canon-radius-2);
box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1), inset -4px -2px 0 0 var(--canon-bg-surface-1);
padding-right: var(--canon-space-3);
}
}
.table-cell {
padding: var(--canon-space-3);
background-color: var(--canon-bg);
box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1), inset 0px -2px 0 0 var(--canon-bg-surface-1);
}
+1
View File
@@ -56,6 +56,7 @@
"@storybook/react": "^8.6.8",
"@storybook/react-webpack5": "^8.6.8",
"@storybook/test": "^8.6.8",
"@tanstack/react-table": "^8.21.2",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"chalk": "^5.4.1",
+111 -33
View File
@@ -17,6 +17,7 @@ import { ReactNode } from 'react';
import { RefAttributes } from 'react';
import type { RemixiconComponentType } from '@remixicon/react';
import { ScrollArea as ScrollArea_2 } from '@base-ui-components/react/scroll-area';
import { Table as Table_2 } from '@tanstack/react-table';
import { TdHTMLAttributes } from 'react';
import { ThHTMLAttributes } from 'react';
import { Tooltip as Tooltip_2 } from '@base-ui-components/react/tooltip';
@@ -219,6 +220,65 @@ export interface ContainerProps {
style?: React.CSSProperties;
}
// @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>
>;
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';
@@ -1028,43 +1088,61 @@ export type StylingPropDef = {
parseValue?: (value: string) => string | undefined;
};
// @public (undocumented)
export const Table: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableElement> & RefAttributes<HTMLTableElement>
>;
// @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>
>;
Caption: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableCaptionElement> &
RefAttributes<HTMLTableCaptionElement>
>;
};
// @public (undocumented)
export const TableBody: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableSectionElement> &
RefAttributes<HTMLTableSectionElement>
>;
export interface TableCellLinkProps
extends React.TdHTMLAttributes<HTMLTableCellElement> {
// (undocumented)
description?: string;
// (undocumented)
href: string;
// (undocumented)
render?: useRender.ComponentProps<'a'>['render'];
// (undocumented)
title: string;
}
// @public (undocumented)
export const TableCell: ForwardRefExoticComponent<
TdHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
>;
// @public (undocumented)
export const TableFooter: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableSectionElement> &
RefAttributes<HTMLTableSectionElement>
>;
// @public (undocumented)
export const TableHead: ForwardRefExoticComponent<
ThHTMLAttributes<HTMLTableCellElement> & RefAttributes<HTMLTableCellElement>
>;
// @public (undocumented)
export const TableHeader: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableSectionElement> &
RefAttributes<HTMLTableSectionElement>
>;
// @public (undocumented)
export const TableRow: ForwardRefExoticComponent<
HTMLAttributes<HTMLTableRowElement> & RefAttributes<HTMLTableRowElement>
>;
export interface TableCellTextProps
extends React.TdHTMLAttributes<HTMLTableCellElement> {
// (undocumented)
description?: string;
// (undocumented)
title: string;
}
// @public (undocumented)
const Text_2: ForwardRefExoticComponent<
@@ -0,0 +1,145 @@
/*
* 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 { DataTable } from '.';
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';
const meta = {
title: 'Components/DataTable',
} 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.Root table={table}>
<DataTable.Table />
<DataTable.Pagination />
</DataTable.Root>
);
},
};
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.Root table={table}>
<DataTable.Table />
<DataTable.Pagination />
</DataTable.Root>
);
},
};
export const WithCustomTable: Story = {
render: () => {
const table = useReactTable<DataProps>({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
return (
<DataTable.Root table={table}>
<DataTable.TableRoot>
<DataTable.TableHeader>
{table.getHeaderGroups().map(headerGroup => (
<DataTable.TableRow key={headerGroup.id}>
{headerGroup.headers.map(header => {
return (
<DataTable.TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</DataTable.TableHead>
);
})}
</DataTable.TableRow>
))}
</DataTable.TableHeader>
<DataTable.TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map(row => (
<DataTable.TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
>
{row.getVisibleCells().map(cell => (
<DataTable.TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</DataTable.TableCell>
))}
</DataTable.TableRow>
))
) : (
<DataTable.TableRow>
<DataTable.TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</DataTable.TableCell>
</DataTable.TableRow>
)}
</DataTable.TableBody>
</DataTable.TableRoot>
<DataTable.Pagination />
</DataTable.Root>
);
},
};
@@ -0,0 +1,50 @@
/*
* 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 { forwardRef } from 'react';
import { Table } from '../Table';
import { DataTableRoot } from './Root/DataTableRoot';
import { DataTablePagination } from './Pagination/DataTablePagination';
import { Table as TanstackTable } from '@tanstack/react-table';
import { DataTableTable } from './Table/DataTableTable';
const TableRoot = forwardRef<
React.ElementRef<typeof Table.Root>,
React.ComponentPropsWithoutRef<typeof Table.Root>
>(({ className, ...props }, ref) => <Table.Root ref={ref} {...props} />);
TableRoot.displayName = Table.Root.displayName;
/**
* DataTable component for displaying tabular data with pagination
* @public
*/
export const DataTable = {
Root: DataTableRoot as <TData>(
props: {
table: TanstackTable<TData>;
} & React.HTMLAttributes<HTMLDivElement>,
) => JSX.Element,
Pagination: DataTablePagination,
Table: DataTableTable,
TableRoot: TableRoot,
TableHeader: Table.Header,
TableBody: Table.Body,
TableRow: Table.Row,
TableCell: Table.Cell,
TableCellText: Table.CellText,
TableCellLink: Table.CellLink,
TableHead: Table.Head,
};
@@ -0,0 +1,55 @@
/*
* 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 { DataTablePagination } from './DataTablePagination';
import {
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from '@tanstack/react-table';
import { data, DataProps } from '../mocked-components';
import { columns } from '../mocked-columns';
import { DataTable } from '../DataTable';
const meta = {
title: 'Components/DataTable/Pagination',
component: DataTablePagination,
} satisfies Meta<typeof DataTablePagination>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
render: () => {
const table = useReactTable<DataProps>({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
});
return (
<DataTable.Root table={table}>
<DataTable.Pagination />
</DataTable.Root>
);
},
};
@@ -0,0 +1,23 @@
.canon-DataTablePagination {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: var(--canon-space-5);
}
.canon-DataTablePagination--left {
display: flex;
align-items: center;
justify-content: space-between;
}
.canon-DataTablePagination--right {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--canon-space-2);
}
.canon-DataTablePagination--select {
min-width: 10.5rem;
}
@@ -0,0 +1,90 @@
/*
* 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 { forwardRef } from 'react';
import { Text } from '../../Text';
import { DataTablePaginationProps } from './types';
import { IconButton } from '../../IconButton';
import clsx from 'clsx';
import { Select } from '../../Select';
import { useDataTable } from '../Root/DataTableRoot';
/** @public */
const DataTablePagination = forwardRef(
(
props: DataTablePaginationProps,
ref: React.ForwardedRef<HTMLDivElement>,
) => {
const { className, ...rest } = props;
const { table } = useDataTable();
const pageIndex = table?.getState().pagination.pageIndex;
const pageSize = table?.getState().pagination.pageSize;
return (
<div
ref={ref}
className={clsx('canon-DataTablePagination', className)}
{...rest}
>
<div className="canon-DataTablePagination--left">
{!table.options.manualPagination && (
<Select
name="pageSize"
size="small"
placeholder="Show 10 results"
options={[
{ label: 'Show 5 results', value: '5' },
{ label: 'Show 10 results', value: '10' },
{ label: 'Show 20 results', value: '20' },
{ label: 'Show 30 results', value: '30' },
{ label: 'Show 40 results', value: '40' },
{ label: 'Show 50 results', value: '50' },
]}
value={pageSize?.toString()}
onValueChange={value => {
table?.setPageSize(Number(value));
}}
className="canon-DataTablePagination--select"
/>
)}
</div>
<div className="canon-DataTablePagination--right">
<Text variant="body">{`${(pageIndex ?? 0) * (pageSize ?? 10) + 1} - ${
((pageIndex ?? 0) + 1) * (pageSize ?? 10)
} of ${table?.getRowCount()}`}</Text>
<IconButton
variant="secondary"
size="small"
onClick={() => table?.previousPage()}
disabled={!table?.getCanPreviousPage()}
icon="chevron-left"
/>
<IconButton
variant="secondary"
size="small"
onClick={() => table?.nextPage()}
disabled={!table?.getCanNextPage()}
icon="chevron-right"
/>
</div>
</div>
);
},
);
DataTablePagination.displayName = 'DataTablePagination';
export { DataTablePagination };
@@ -0,0 +1,19 @@
/*
* 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.
*/
/** @public */
export interface DataTablePaginationProps
extends React.HTMLAttributes<HTMLDivElement> {}
@@ -0,0 +1,5 @@
.canon-DataTableRoot {
display: flex;
flex-direction: column;
gap: var(--canon-space-3);
}
@@ -0,0 +1,62 @@
/*
* 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 { forwardRef, createContext, useContext } from 'react';
import clsx from 'clsx';
import { DataTableRootProps } from './types';
import { Table } from '@tanstack/react-table';
type DataTableContextType<TData> = {
table: Table<TData>;
};
/** @public */
export const DataTableContext = createContext<DataTableContextType<any> | null>(
null,
);
/** @public */
const DataTableRoot = forwardRef(
<TData,>(
props: DataTableRootProps<TData>,
ref: React.ForwardedRef<HTMLDivElement>,
) => {
const { className, table, ...rest } = props;
return (
<DataTableContext.Provider value={{ table }}>
<div
ref={ref}
className={clsx('canon-DataTable', className)}
{...rest}
/>
</DataTableContext.Provider>
);
},
);
DataTableRoot.displayName = 'DataTableRoot';
export { DataTableRoot };
/** @public */
export function useDataTable<TData>() {
const context = useContext(DataTableContext);
if (!context) {
throw new Error('useDataTable must be used within a DataTableRoot');
}
return context as DataTableContextType<TData>;
}
@@ -0,0 +1,26 @@
/*
* 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 { Table } from '@tanstack/react-table';
/** @public */
export interface DataTableRootProps<TData>
extends React.HTMLAttributes<HTMLDivElement> {
/**
* The table instance.
*/
table: Table<TData>;
}
@@ -0,0 +1,82 @@
/*
* 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 { forwardRef } from 'react';
import clsx from 'clsx';
import { DataTableTableProps } from './types';
import { Table } from '../../Table';
import { useDataTable } from '../Root/DataTableRoot';
import { flexRender } from '@tanstack/react-table';
/** @public */
const DataTableTable = forwardRef(
(props: DataTableTableProps, ref: React.ForwardedRef<HTMLTableElement>) => {
const { className, ...rest } = props;
const { table } = useDataTable();
return (
<Table.Root ref={ref} className={clsx(className)} {...rest}>
<Table.Header>
{table.getHeaderGroups().map(headerGroup => (
<Table.Row key={headerGroup.id}>
{headerGroup.headers.map(header => {
return (
<Table.Head key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</Table.Head>
);
})}
</Table.Row>
))}
</Table.Header>
<Table.Body>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map(row => (
<Table.Row
key={row.id}
data-state={row.getIsSelected() && 'selected'}
>
{row.getVisibleCells().map(cell => (
<Table.Cell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</Table.Cell>
))}
</Table.Row>
))
) : (
<Table.Row>
<Table.Cell
colSpan={table.getAllColumns().length}
className="h-24 text-center"
>
No results.
</Table.Cell>
</Table.Row>
)}
</Table.Body>
</Table.Root>
);
},
);
DataTableTable.displayName = 'DataTableRoot';
export { DataTableTable };
@@ -0,0 +1,19 @@
/*
* 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.
*/
/** @public */
export interface DataTableTableProps
extends React.HTMLAttributes<HTMLTableElement> {}
@@ -0,0 +1,20 @@
/*
* 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.
*/
export * from './DataTable';
export * from './Root/types';
export * from './Pagination/types';
export * from './Table/types';
@@ -0,0 +1,74 @@
/*
* 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 { ColumnDef } from '@tanstack/react-table';
import { DataProps } from './mocked-components';
import { Checkbox } from '../Checkbox';
import { DataTable } from './DataTable';
export const columns: ColumnDef<DataProps>[] = [
{
id: 'select',
header: ({ table }) => (
<Checkbox
checked={table.getIsAllPageRowsSelected()}
onChange={(checked: boolean) =>
table.toggleAllPageRowsSelected(checked)
}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<td>
<Checkbox
checked={row.getIsSelected()}
onChange={(checked: boolean) => row.toggleSelected(checked)}
aria-label="Select row"
/>
</td>
),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: 'name',
header: 'Name',
cell: ({ row }) => (
<DataTable.TableCellLink
title={row.getValue('name')}
description={row.original.description}
href="/"
/>
),
},
{
accessorKey: 'owner',
header: 'Owner',
cell: ({ row }) => (
<DataTable.TableCellText title={row.getValue('owner')} />
),
},
{
accessorKey: 'type',
header: 'Type',
cell: ({ row }) => <DataTable.TableCellText title={row.getValue('type')} />,
},
{
accessorKey: 'tags',
header: 'Tags',
cell: ({ row }) => <DataTable.TableCellText title={row.getValue('tags')} />,
},
];
@@ -0,0 +1,818 @@
/*
* 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.
*/
export interface DataProps {
name: string;
owner: string;
type: 'documentation' | 'library' | 'service' | 'website' | 'other';
description?: string;
tags?: string[];
}
export const data: DataProps[] = [
{
name: 'authentication-and-authorization-service',
owner: 'security-team',
type: 'service',
description:
'A comprehensive service handling user authentication and role-based access control across all applications.',
tags: ['security', 'authentication', 'authorization'],
},
{
name: 'user-interface-dashboard-and-analytics-platform',
owner: 'frontend-team',
type: 'website',
description:
'Interactive dashboard providing real-time analytics and data visualization for business metrics.',
tags: ['analytics', 'visualization', 'dashboard'],
},
{
name: 'payment-gateway',
owner: 'finance-team',
type: 'service',
description:
'Secure payment processing system supporting multiple payment methods and currencies.',
tags: ['payments', 'security', 'finance'],
},
{
name: 'real-time-analytics-processing-and-visualization-engine',
owner: 'data-team',
type: 'service',
description:
'High-performance engine for processing and visualizing streaming data analytics.',
tags: ['analytics', 'real-time', 'data-processing'],
},
{
name: 'notification-center',
owner: 'platform-team',
type: 'service',
description:
'Centralized system for managing and delivering notifications across multiple channels.',
tags: ['notifications', 'messaging'],
},
{
name: 'administrative-control-panel-and-user-management-interface',
owner: 'frontend-team',
type: 'website',
description:
'Admin interface for managing users, permissions, and system configurations.',
tags: ['admin', 'user-management', 'configuration'],
},
{
name: 'search-indexer',
owner: 'search-team',
type: 'service',
description:
'Service responsible for indexing and updating searchable content across the platform.',
tags: ['search', 'indexing'],
},
{
name: 'cross-platform-mobile-application-framework',
owner: 'mobile-team',
type: 'website',
description:
'Framework enabling development of cross-platform mobile applications with shared codebase.',
tags: ['mobile', 'framework', 'cross-platform'],
},
{
name: 'database-migration',
owner: 'devops-team',
type: 'other',
description:
'Tools and scripts for managing database schema migrations and data transformations.',
tags: ['database', 'migration', 'devops'],
},
{
name: 'api-gateway',
owner: 'platform-team',
type: 'service',
description:
'Central entry point for all API requests, handling routing, authentication, and rate limiting.',
tags: ['api', 'gateway', 'security', 'routing'],
},
{
name: 'content-management',
owner: 'content-team',
type: 'service',
description:
'System for managing and delivering digital content across multiple channels.',
tags: ['content', 'management', 'delivery'],
},
{
name: 'enterprise-reporting-and-analytics-dashboard',
owner: 'analytics-team',
type: 'website',
description:
'Comprehensive business intelligence platform for enterprise-wide reporting and analytics.',
tags: ['analytics', 'reporting', 'business-intelligence'],
},
{
name: 'image-processing-and-optimization-service',
owner: 'media-team',
type: 'service',
description:
'Service for processing, optimizing, and delivering images across different devices and networks.',
tags: ['media', 'optimization', 'processing'],
},
{
name: 'customer-portal',
owner: 'frontend-team',
type: 'website',
description:
'Self-service portal for customers to manage their accounts and access services.',
tags: ['customer', 'self-service'],
},
{
name: 'log-aggregator',
owner: 'devops-team',
type: 'service',
description:
'Centralized logging system for collecting, processing, and analyzing application logs.',
tags: ['logging', 'monitoring', 'devops'],
},
{
name: 'identity-provider',
owner: 'security-team',
type: 'service',
description:
'Service managing user identities and authentication across the organization.',
tags: ['identity', 'security', 'authentication'],
},
{
name: 'document-storage',
owner: 'storage-team',
type: 'service',
description:
'Secure and scalable document storage system with version control and access management.',
tags: ['storage', 'documents', 'version-control'],
},
{
name: 'workflow-engine',
owner: 'platform-team',
type: 'service',
description:
'Engine for defining and executing business processes and workflows.',
tags: ['workflow', 'automation'],
},
{
name: 'mobile-backend',
owner: 'mobile-team',
type: 'service',
description:
'Backend services supporting mobile applications with optimized APIs and data synchronization.',
tags: ['mobile', 'backend', 'api'],
},
{
name: 'system-monitoring-and-alerting-dashboard',
owner: 'devops-team',
type: 'website',
description:
'Real-time monitoring and alerting system for infrastructure and application health.',
tags: ['monitoring', 'alerting', 'devops', 'infrastructure'],
},
{
name: 'email-service',
owner: 'communication-team',
type: 'service',
description:
'Reliable email delivery service with templates and tracking capabilities.',
tags: ['email', 'communication'],
},
{
name: 'data-pipeline',
owner: 'data-team',
type: 'service',
description:
'ETL pipeline for processing and transforming large volumes of data.',
tags: ['data', 'etl', 'pipeline'],
},
{
name: 'configuration-manager',
owner: 'platform-team',
type: 'service',
description:
'Centralized system for managing application configurations across environments.',
tags: ['configuration', 'management'],
},
{
name: 'testing-framework',
owner: 'qa-team',
type: 'library',
description:
'Comprehensive testing framework supporting various types of automated tests.',
tags: ['testing', 'automation', 'qa'],
},
{
name: 'cache-service',
owner: 'platform-team',
type: 'service',
description:
'Distributed caching service for improving application performance.',
tags: ['caching', 'performance'],
},
{
name: 'billing-system',
owner: 'finance-team',
type: 'service',
description:
'System for managing customer billing, invoicing, and payment processing.',
tags: ['billing', 'finance', 'payments'],
},
{
name: 'comprehensive-product-documentation-and-api-reference',
owner: 'docs-team',
type: 'documentation',
description:
'Complete documentation covering product features, APIs, and integration guides.',
tags: ['documentation', 'api', 'reference'],
},
{
name: 'queue-manager',
owner: 'platform-team',
type: 'service',
description:
'Message queue system for asynchronous processing and event handling.',
tags: ['queue', 'messaging', 'async'],
},
{
name: 'security-scanner',
owner: 'security-team',
type: 'other',
description:
'Automated security scanning tool for identifying vulnerabilities in code and infrastructure.',
tags: ['security', 'scanning', 'vulnerability'],
},
{
name: 'user-profile',
owner: 'frontend-team',
type: 'website',
description:
'User profile management interface with personalization features.',
tags: ['user', 'profile', 'personalization'],
},
{
name: 'data-warehouse',
owner: 'data-team',
type: 'service',
description:
'Centralized data repository for business intelligence and analytics.',
tags: ['data', 'warehouse', 'analytics'],
},
{
name: 'deployment-automation',
owner: 'devops-team',
type: 'other',
description:
'Automated deployment pipeline for continuous integration and delivery.',
tags: ['deployment', 'automation', 'ci-cd', 'devops'],
},
{
name: 'chat-service',
owner: 'communication-team',
type: 'service',
description:
'Real-time chat service supporting text, file sharing, and group conversations.',
tags: ['chat', 'communication', 'real-time'],
},
{
name: 'analytics-dashboard',
owner: 'analytics-team',
type: 'website',
description:
'Interactive dashboard for visualizing and analyzing business metrics.',
tags: ['analytics', 'dashboard', 'visualization'],
},
{
name: 'file-uploader',
owner: 'storage-team',
type: 'service',
description:
'Service for handling secure file uploads with progress tracking and validation.',
tags: ['storage', 'upload', 'files'],
},
{
name: 'search-service',
owner: 'search-team',
type: 'service',
description:
'Full-text search service with advanced filtering and ranking capabilities.',
tags: ['search', 'full-text'],
},
{
name: 'mobile-sdk',
owner: 'mobile-team',
type: 'library',
description:
'Software development kit for building mobile applications with native features.',
tags: ['mobile', 'sdk', 'development'],
},
{
name: 'performance-monitor',
owner: 'devops-team',
type: 'service',
description:
'System for monitoring and analyzing application performance metrics.',
tags: ['performance', 'monitoring', 'metrics'],
},
{
name: 'content-delivery',
owner: 'media-team',
type: 'service',
description:
'CDN service for optimized content delivery across global networks.',
tags: ['cdn', 'content', 'delivery'],
},
{
name: 'user-authentication',
owner: 'security-team',
type: 'service',
description:
'Service handling user login, session management, and authentication flows.',
tags: ['authentication', 'security', 'user'],
},
{
name: 'data-export',
owner: 'data-team',
type: 'service',
description:
'Service for exporting data in various formats with scheduling capabilities.',
tags: ['data', 'export', 'scheduling'],
},
{
name: 'admin-api',
owner: 'platform-team',
type: 'service',
description:
'API endpoints for administrative functions and system management.',
tags: ['api', 'admin', 'management'],
},
{
name: 'testing-dashboard',
owner: 'qa-team',
type: 'website',
description: 'Dashboard for monitoring test results and quality metrics.',
tags: ['testing', 'dashboard', 'qa'],
},
{
name: 'message-broker',
owner: 'platform-team',
type: 'service',
description:
'Message broker service for reliable event-driven communication between services.',
tags: ['messaging', 'broker', 'event-driven'],
},
{
name: 'payment-processor',
owner: 'finance-team',
type: 'service',
description:
'Service for processing financial transactions and payment methods.',
tags: ['payments', 'finance', 'processing'],
},
{
name: 'document-viewer',
owner: 'frontend-team',
type: 'website',
description: 'Web-based document viewer supporting multiple file formats.',
tags: ['documents', 'viewer'],
},
{
name: 'load-balancer',
owner: 'devops-team',
type: 'service',
description:
'Service for distributing network traffic across multiple servers.',
tags: ['load-balancing', 'networking', 'infrastructure'],
},
{
name: 'security-audit',
owner: 'security-team',
type: 'other',
description:
'Tools and processes for conducting security audits and compliance checks.',
tags: ['security', 'audit', 'compliance'],
},
{
name: 'user-settings',
owner: 'frontend-team',
type: 'website',
description:
'Interface for users to manage their preferences and account settings.',
tags: ['user', 'settings', 'preferences'],
},
{
name: 'data-import',
owner: 'data-team',
type: 'service',
description:
'Service for importing and validating data from external sources.',
tags: ['data', 'import', 'validation'],
},
{
name: 'infrastructure-monitor',
owner: 'devops-team',
type: 'service',
description:
'Monitoring system for infrastructure components and resources.',
tags: ['monitoring', 'infrastructure', 'devops'],
},
{
name: 'notification-manager',
owner: 'communication-team',
type: 'service',
description:
'Service for managing and delivering notifications across multiple channels.',
tags: ['notifications', 'management'],
},
{
name: 'analytics-processor',
owner: 'analytics-team',
type: 'service',
description:
'Service for processing and analyzing business data and metrics.',
tags: ['analytics', 'processing', 'metrics'],
},
{
name: 'file-manager',
owner: 'storage-team',
type: 'website',
description: 'Web interface for managing files and storage resources.',
tags: ['files', 'storage', 'management'],
},
{
name: 'search-index',
owner: 'search-team',
type: 'service',
description: 'Service for maintaining and updating search indices.',
tags: ['search', 'indexing'],
},
{
name: 'mobile-authentication',
owner: 'mobile-team',
type: 'service',
description:
'Authentication service specifically designed for mobile applications.',
tags: ['mobile', 'authentication', 'security'],
},
{
name: 'system-monitor',
owner: 'devops-team',
type: 'service',
description:
'Monitoring service for system health and performance metrics.',
tags: ['monitoring', 'system', 'metrics'],
},
{
name: 'media-processor',
owner: 'media-team',
type: 'service',
description: 'Service for processing and optimizing media files.',
tags: ['media', 'processing', 'optimization'],
},
{
name: 'user-management',
owner: 'security-team',
type: 'service',
description: 'Service for managing user accounts and permissions.',
tags: ['user', 'management', 'security'],
},
{
name: 'data-transformer',
owner: 'data-team',
type: 'service',
description:
'Service for transforming data between different formats and structures.',
tags: ['data', 'transformation'],
},
{
name: 'admin-dashboard',
owner: 'platform-team',
type: 'website',
description:
'Administrative interface for system management and monitoring.',
tags: ['admin', 'dashboard', 'management'],
},
{
name: 'test-automation',
owner: 'qa-team',
type: 'other',
description: 'Tools and frameworks for automating testing processes.',
tags: ['testing', 'automation', 'qa'],
},
{
name: 'event-bus',
owner: 'platform-team',
type: 'service',
description: 'Event-driven communication system between services.',
tags: ['events', 'messaging', 'communication'],
},
{
name: 'invoice-generator',
owner: 'finance-team',
type: 'service',
description: 'Service for generating and managing invoices.',
tags: ['invoices', 'finance'],
},
{
name: 'document-editor',
owner: 'frontend-team',
type: 'website',
description: 'Web-based document editing interface.',
tags: ['documents', 'editor'],
},
{
name: 'service-discovery',
owner: 'devops-team',
type: 'service',
description: 'Service for discovering and registering available services.',
tags: ['discovery', 'services', 'devops'],
},
{
name: 'security-monitor',
owner: 'security-team',
type: 'service',
description: 'Service for monitoring security events and threats.',
tags: ['security', 'monitoring', 'threats'],
},
{
name: 'user-preferences',
owner: 'frontend-team',
type: 'website',
description: 'Interface for managing user preferences and settings.',
tags: ['user', 'preferences'],
},
{
name: 'data-validator',
owner: 'data-team',
type: 'service',
description: 'Service for validating data integrity and format.',
tags: ['data', 'validation'],
},
{
name: 'infrastructure-automation',
owner: 'devops-team',
type: 'other',
description:
'Tools for automating infrastructure provisioning and management.',
tags: ['infrastructure', 'automation', 'devops'],
},
{
name: 'notification-dispatcher',
owner: 'communication-team',
type: 'service',
description:
'Service for dispatching notifications to appropriate channels.',
tags: ['notifications', 'dispatch'],
},
{
name: 'analytics-collector',
owner: 'analytics-team',
type: 'service',
description: 'Service for collecting and aggregating analytics data.',
tags: ['analytics', 'collection', 'aggregation'],
},
{
name: 'file-processor',
owner: 'storage-team',
type: 'service',
description: 'Service for processing and managing files.',
tags: ['files', 'processing'],
},
{
name: 'search-analyzer',
owner: 'search-team',
type: 'service',
description: 'Service for analyzing search queries and results.',
tags: ['search', 'analysis'],
},
{
name: 'mobile-notifications',
owner: 'mobile-team',
type: 'service',
description: 'Service for sending notifications to mobile devices.',
tags: ['mobile', 'notifications'],
},
{
name: 'system-alerts',
owner: 'devops-team',
type: 'service',
description: 'Service for managing and dispatching system alerts.',
tags: ['alerts', 'system', 'monitoring'],
},
{
name: 'media-encoder',
owner: 'media-team',
type: 'service',
description: 'Service for encoding and processing media files.',
tags: ['media', 'encoding'],
},
{
name: 'user-authorization',
owner: 'security-team',
type: 'service',
description: 'Service for managing user permissions and access control.',
tags: ['authorization', 'security', 'user'],
},
{
name: 'data-aggregator',
owner: 'data-team',
type: 'service',
description: 'Service for aggregating data from multiple sources.',
tags: ['data', 'aggregation'],
},
{
name: 'admin-authentication',
owner: 'platform-team',
type: 'service',
description: 'Authentication service for administrative access.',
tags: ['admin', 'authentication', 'security'],
},
{
name: 'test-coverage',
owner: 'qa-team',
type: 'other',
description: 'Tools for measuring and reporting test coverage.',
tags: ['testing', 'coverage', 'qa'],
},
{
name: 'event-processor',
owner: 'platform-team',
type: 'service',
description: 'Service for processing and handling events.',
tags: ['events', 'processing'],
},
{
name: 'payment-validator',
owner: 'finance-team',
type: 'service',
description: 'Service for validating payment transactions.',
tags: ['payments', 'validation', 'finance'],
},
{
name: 'document-converter',
owner: 'frontend-team',
type: 'service',
description: 'Service for converting documents between different formats.',
tags: ['documents', 'conversion'],
},
{
name: 'service-health',
owner: 'devops-team',
type: 'service',
description: 'Service for monitoring and reporting service health status.',
tags: ['health', 'monitoring', 'services'],
},
{
name: 'security-logger',
owner: 'security-team',
type: 'service',
description: 'Service for logging security-related events and activities.',
tags: ['security', 'logging'],
},
{
name: 'user-analytics',
owner: 'frontend-team',
type: 'website',
description:
'Analytics dashboard for user behavior and engagement metrics.',
tags: ['analytics', 'user', 'metrics'],
},
{
name: 'data-cleaner',
owner: 'data-team',
type: 'service',
description: 'Service for cleaning and standardizing data.',
tags: ['data', 'cleaning'],
},
{
name: 'infrastructure-deployer',
owner: 'devops-team',
type: 'other',
description: 'Tools for deploying and managing infrastructure resources.',
tags: ['infrastructure', 'deployment', 'devops'],
},
{
name: 'notification-queue',
owner: 'communication-team',
type: 'service',
description: 'Queue system for managing notification delivery.',
tags: ['notifications', 'queue'],
},
{
name: 'analytics-exporter',
owner: 'analytics-team',
type: 'service',
description: 'Service for exporting analytics data in various formats.',
tags: ['analytics', 'export'],
},
{
name: 'file-validator',
owner: 'storage-team',
type: 'service',
description: 'Service for validating file integrity and format.',
tags: ['files', 'validation'],
},
{
name: 'search-optimizer',
owner: 'search-team',
type: 'service',
description: 'Service for optimizing search performance and relevance.',
tags: ['search', 'optimization'],
},
{
name: 'mobile-analytics',
owner: 'mobile-team',
type: 'service',
description: 'Analytics service specifically for mobile applications.',
tags: ['mobile', 'analytics'],
},
{
name: 'system-logger',
owner: 'devops-team',
type: 'service',
description: 'Service for logging system events and activities.',
tags: ['logging', 'system'],
},
{
name: 'media-validator',
owner: 'media-team',
type: 'service',
description: 'Service for validating media files and formats.',
tags: ['media', 'validation'],
},
{
name: 'user-audit',
owner: 'security-team',
type: 'service',
description: 'Service for auditing user activities and access.',
tags: ['audit', 'user', 'security'],
},
{
name: 'data-normalizer',
owner: 'data-team',
type: 'service',
description: 'Service for normalizing data formats and structures.',
tags: ['data', 'normalization'],
},
{
name: 'admin-authorization',
owner: 'platform-team',
type: 'service',
description: 'Authorization service for administrative functions.',
tags: ['admin', 'authorization', 'security'],
},
{
name: 'test-reporting',
owner: 'qa-team',
type: 'other',
description: 'Tools for generating and managing test reports.',
tags: ['testing', 'reporting', 'qa'],
},
{
name: 'event-aggregator',
owner: 'platform-team',
type: 'service',
description: 'Service for aggregating and processing events.',
tags: ['events', 'aggregation'],
},
{
name: 'payment-reconciler',
owner: 'finance-team',
type: 'service',
description: 'Service for reconciling payment transactions.',
tags: ['payments', 'reconciliation', 'finance'],
},
{
name: 'document-validator',
owner: 'frontend-team',
type: 'service',
description: 'Service for validating document formats and content.',
tags: ['documents', 'validation'],
},
{
name: 'service-monitor',
owner: 'devops-team',
type: 'service',
description: 'Service for monitoring service health and performance.',
tags: ['monitoring', 'services', 'health'],
},
{
name: 'security-validator',
owner: 'security-team',
type: 'service',
description: 'Service for validating security configurations and policies.',
tags: ['security', 'validation'],
},
];
@@ -14,6 +14,10 @@
* limitations under the License.
*/
import { ComponentType } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { Table } from '../Table';
const invoices = [
{
invoice: 'INV001',
@@ -59,28 +63,15 @@ const invoices = [
},
];
import { ComponentType } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import {
Table,
TableBody,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
} from '../Table';
const meta = {
title: 'Components/Table',
component: Table,
component: Table.Root,
subcomponents: {
TableBody: TableBody as ComponentType<unknown>,
TableCell: TableCell as ComponentType<unknown>,
TableFooter: TableFooter as ComponentType<unknown>,
TableHead: TableHead as ComponentType<unknown>,
TableHeader: TableHeader as ComponentType<unknown>,
TableRow: TableRow as ComponentType<unknown>,
Body: Table.Body as ComponentType<unknown>,
Cell: Table.Cell as ComponentType<unknown>,
Head: Table.Head as ComponentType<unknown>,
Header: Table.Header as ComponentType<unknown>,
Row: Table.Row as ComponentType<unknown>,
},
} satisfies Meta<typeof Table>;
@@ -89,31 +80,27 @@ 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>
<Table.Root>
<Table.Header>
<Table.Row>
<Table.Head className="w-[100px]">Invoice</Table.Head>
<Table.Head>Status</Table.Head>
<Table.Head>Method</Table.Head>
<Table.Head className="text-right">Amount</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{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>
<Table.Row key={invoice.invoice}>
<Table.Cell className="font-medium">{invoice.invoice}</Table.Cell>
<Table.Cell>{invoice.paymentStatus}</Table.Cell>
<Table.Cell>{invoice.paymentMethod}</Table.Cell>
<Table.Cell className="text-right">
{invoice.totalAmount}
</Table.Cell>
</Table.Row>
))}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total</TableCell>
<TableCell className="text-right">$2,500.00</TableCell>
</TableRow>
</TableFooter>
</Table>
</Table.Body>
</Table.Root>
),
};
+28 -48
View File
@@ -13,103 +13,83 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { forwardRef } from 'react';
/** @public */
const Table = forwardRef<
import { forwardRef } from 'react';
import clsx from 'clsx';
import { TableCell } from './TableCell/TableCell';
import { TableCellText } from './TableCellText/TableCellText';
import { TableCellLink } from './TableCellLink/TableCellLink';
const TableRoot = forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="table">
<table ref={ref} className={className} {...props} />
</div>
<table ref={ref} className={clsx('canon-TableRoot', className)} {...props} />
));
Table.displayName = 'Table';
TableRoot.displayName = 'TableRoot';
/** @public */
const TableHeader = forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead
ref={ref}
className={['table-header', className].join(' ')}
className={clsx('canon-TableHeader', className)}
{...props}
/>
));
TableHeader.displayName = 'TableHeader';
/** @public */
const TableBody = forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody ref={ref} className={['table-body', className].join(' ')} {...props} />
<tbody ref={ref} className={clsx('canon-TableBody', className)} {...props} />
));
TableBody.displayName = 'TableBody';
/** @public */
const TableFooter = forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={['table-footer', className].join(' ')}
{...props}
/>
));
TableFooter.displayName = 'TableFooter';
/** @public */
const TableRow = forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr ref={ref} className={['table-row', className].join(' ')} {...props}>
<tr ref={ref} className={clsx('canon-TableRow', className)} {...props}>
{props.children}
</tr>
));
TableRow.displayName = 'TableRow';
/** @public */
const TableHead = forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th ref={ref} className={['table-head', className].join(' ')} {...props} />
<th ref={ref} className={clsx('canon-TableHead', className)} {...props} />
));
TableHead.displayName = 'TableHead';
/** @public */
const TableCell = forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td ref={ref} className={['table-cell', className].join(' ')} {...props} />
));
TableCell.displayName = 'TableCell';
/** @public */
const TableCaption = forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={['table-caption', className].join(' ')}
className={clsx('canon-TableCaption', className)}
{...props}
/>
));
TableCaption.displayName = 'TableCaption';
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
/**
* Table component for displaying tabular data
* @public
*/
export const Table = {
Root: TableRoot,
Header: TableHeader,
Body: TableBody,
Head: TableHead,
Row: TableRow,
Cell: TableCell,
CellText: TableCellText,
CellLink: TableCellLink,
Caption: TableCaption,
};
@@ -0,0 +1,32 @@
/*
* 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',
},
};
@@ -0,0 +1,20 @@
/*
* 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.
*/
.canon-TableCell {
padding: var(--canon-space-3);
font-size: var(--canon-font-size-3);
}
@@ -0,0 +1,29 @@
/*
* 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 { forwardRef } from 'react';
import clsx from 'clsx';
/** @public */
const TableCell = forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td ref={ref} className={clsx('canon-TableCell', className)} {...props} />
));
TableCell.displayName = 'TableCell';
export { TableCell };
@@ -0,0 +1,40 @@
/*
* 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 { TableCellLink } from './TableCellLink';
const meta = {
title: 'Components/Table/TableCellLink',
component: TableCellLink,
} satisfies Meta<typeof TableCellLink>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
title: 'I am a link',
href: 'https://canon.backstage.io',
},
};
export const WithDescription: Story = {
args: {
...Default.args,
description: 'This is a description',
},
};
@@ -0,0 +1,21 @@
/*
* 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.
*/
.canon-TableCellLink {
display: flex;
flex-direction: column;
gap: var(--canon-space-0_5);
}
@@ -0,0 +1,46 @@
/*
* 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 { forwardRef } from 'react';
import clsx from 'clsx';
import { TableCellLinkProps } from './types';
import { Text } from '../../Text/Text';
import { Link } from '../../Link/Link';
/** @public */
const TableCellLink = forwardRef<HTMLDivElement, TableCellLinkProps>(
({ className, title, description, href, render, ...props }, ref) => (
<div
ref={ref}
className={clsx('canon-TableCellLink', className)}
{...props}
>
{title && (
<Link href={href} render={render}>
{title}
</Link>
)}
{description && (
<Text variant="body" color="secondary">
{description}
</Text>
)}
</div>
),
);
TableCellLink.displayName = 'TableCellLink';
export { TableCellLink };
@@ -0,0 +1,26 @@
/*
* 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 { useRender } from '@base-ui-components/react/use-render';
/** @public */
export interface TableCellLinkProps
extends React.TdHTMLAttributes<HTMLTableCellElement> {
title: string;
description?: string;
href: string;
render?: useRender.ComponentProps<'a'>['render'];
}
@@ -0,0 +1,39 @@
/*
* 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 { TableCellText } from './TableCellText';
const meta = {
title: 'Components/Table/TableCellText',
component: TableCellText,
} satisfies Meta<typeof TableCellText>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
title: 'Hello world',
},
};
export const WithDescription: Story = {
args: {
...Default.args,
description: 'This is a description',
},
};
@@ -0,0 +1,21 @@
/*
* 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.
*/
.canon-TableCellText {
display: flex;
flex-direction: column;
gap: var(--canon-space-0_5);
}
@@ -0,0 +1,41 @@
/*
* 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 { forwardRef } from 'react';
import clsx from 'clsx';
import { TableCellTextProps } from './types';
import { Text } from '../../Text/Text';
/** @public */
const TableCellText = forwardRef<HTMLDivElement, TableCellTextProps>(
({ className, title, description, ...props }, ref) => (
<div
ref={ref}
className={clsx('canon-TableCellText', className)}
{...props}
>
{title && <Text variant="body">{title}</Text>}
{description && (
<Text variant="body" color="secondary">
{description}
</Text>
)}
</div>
),
);
TableCellText.displayName = 'TableCellText';
export { TableCellText };
@@ -0,0 +1,22 @@
/*
* 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.
*/
/** @public */
export interface TableCellTextProps
extends React.TdHTMLAttributes<HTMLTableCellElement> {
title: string;
description?: string;
}
+4 -9
View File
@@ -13,12 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {
Table,
TableHead,
TableRow,
TableHeader,
TableCell,
TableBody,
TableFooter,
} from './Table';
export * from './Table';
export * from './TableCellText/types';
export * from './TableCellLink/types';
+12 -48
View File
@@ -1,61 +1,25 @@
.table {
position: relative;
overflow: auto;
.canon-TableRoot {
width: 100%;
background-color: var(--canon-bg-surface-1);
border-radius: var(--canon-radius-2);
padding-bottom: var(--canon-space-0_5);
padding-top: var(--canon-space-0_5);
font-size: var(--canon-font-size-3);
font-family: var(--canon-font-regular);
font-weight: var(--canon-font-weight-regular);
table {
width: 100%;
caption-side: bottom;
font-size: var(--canon-font-size-sm);
border-collapse: collapse;
}
caption-side: bottom;
border-collapse: collapse;
}
.table-head {
.canon-TableHead {
text-align: left;
padding: var(--canon-space-3);
font-size: var(--canon-font-size-3);
color: var(--canon-fg-primary);
}
.table-body {
tr:last-child {
border-bottom: none;
}
.canon-TableBody {
color: var(--canon-fg-primary);
}
.table-row {
.canon-TableRow {
border-bottom: 1px solid var(--canon-border);
transition: color 0.2s ease-in-out;
&:hover td {
background-color: var(--canon-bg-tint);
}
& .table-cell:first-child {
border-top-left-radius: var(--canon-radius-2);
border-bottom-left-radius: var(--canon-radius-2);
box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1),
inset 4px -2px 0 0 var(--canon-bg-surface-1);
padding-left: var(--canon-space-3);
}
& .table-cell:last-child {
border-top-right-radius: var(--canon-radius-2);
border-bottom-right-radius: var(--canon-radius-2);
box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1),
inset -4px -2px 0 0 var(--canon-bg-surface-1);
padding-right: var(--canon-space-3);
}
}
.table-cell {
padding: var(--canon-space-3);
background-color: var(--canon-bg);
box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1),
inset 0px -2px 0 0 var(--canon-bg-surface-1);
.canon-TableBody .canon-TableRow:hover {
background-color: var(--canon-gray-2);
}
+5
View File
@@ -16,12 +16,17 @@
@import '../components/Box/styles.css';
@import '../components/Button/styles.css';
@import '../components/DataTable/Root/DataTableRoot.styles.css';
@import '../components/DataTable/Pagination/DataTablePagination.styles.css';
@import '../components/Flex/styles.css';
@import '../components/Grid/styles.css';
@import '../components/Container/styles.css';
@import '../components/Icon/styles.css';
@import '../components/Checkbox/styles.css';
@import '../components/Table/styles.css';
@import '../components/Table/TableCell/TableCell.styles.css';
@import '../components/Table/TableCellText/TableCellText.styles.css';
@import '../components/Table/TableCellLink/TableCellLink.styles.css';
@import '../components/Text/styles.css';
@import '../components/Heading/styles.css';
@import '../components/IconButton/styles.css';
+1
View File
@@ -33,6 +33,7 @@ export * from './components/Heading';
// UI components
export * from './components/Button';
export * from './components/DataTable';
export * from './components/Icon';
export * from './components/IconButton';
export * from './components/Checkbox';
+27 -7
View File
@@ -3789,6 +3789,7 @@ __metadata:
"@storybook/react": "npm:^8.6.8"
"@storybook/react-webpack5": "npm:^8.6.8"
"@storybook/test": "npm:^8.6.8"
"@tanstack/react-table": "npm:^8.21.2"
"@types/react": "npm:^18.0.0"
"@types/react-dom": "npm:^18.0.0"
chalk: "npm:^5.4.1"
@@ -16446,20 +16447,20 @@ __metadata:
linkType: hard
"@rspack/dev-server@npm:^1.0.9":
version: 1.1.1
resolution: "@rspack/dev-server@npm:1.1.1"
version: 1.1.0
resolution: "@rspack/dev-server@npm:1.1.0"
dependencies:
chokidar: "npm:^3.6.0"
express: "npm:^4.21.2"
http-proxy-middleware: "npm:^2.0.7"
express: "npm:^4.19.2"
http-proxy-middleware: "npm:^2.0.6"
mime-types: "npm:^2.1.35"
p-retry: "npm:^6.2.0"
webpack-dev-middleware: "npm:^7.4.2"
webpack-dev-server: "npm:5.2.0"
ws: "npm:^8.18.0"
ws: "npm:^8.16.0"
peerDependencies:
"@rspack/core": "*"
checksum: 10/a712c1ed6819da26984d106e7fdede3ed720ef177bf8fa976e91eb95988b862b306c354829486dce8eabde420cb5a96cb25ecf397c9532f87f3b913e9a9da40c
checksum: 10/9619dc416e4bb46f13a47c62725ab538ef8c1e02240437bf91394b6dbe3f0f915ad05c5bdda72501b813cb9ff86989bf9b295efee8e39323f6626573f53af459
languageName: node
linkType: hard
@@ -19237,6 +19238,25 @@ __metadata:
languageName: node
linkType: hard
"@tanstack/react-table@npm:^8.21.2":
version: 8.21.2
resolution: "@tanstack/react-table@npm:8.21.2"
dependencies:
"@tanstack/table-core": "npm:8.21.2"
peerDependencies:
react: ">=16.8"
react-dom: ">=16.8"
checksum: 10/62b944b79f0ac06b74921c1da4821a4f122f7b30e2bd3391b5eac2d15a8050f3ac7752282354520ec42a3fa382580ac88e28731121ad445bb65218614ab21fd1
languageName: node
linkType: hard
"@tanstack/table-core@npm:8.21.2":
version: 8.21.2
resolution: "@tanstack/table-core@npm:8.21.2"
checksum: 10/6a435905fcfa2ac0d06963f14125f313a08086cb0c71949d60fbd04c3a5e229779f94ff6783e35cf09f77fddfbe987cfdd74b18577ac7476b5a43729f3e4b9d5
languageName: node
linkType: hard
"@techdocs/cli@workspace:*, @techdocs/cli@workspace:packages/techdocs-cli":
version: 0.0.0-use.local
resolution: "@techdocs/cli@workspace:packages/techdocs-cli"
@@ -48115,7 +48135,7 @@ __metadata:
languageName: node
linkType: hard
"ws@npm:*, ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.17.1, ws@npm:^8.18.0, ws@npm:^8.2.3, ws@npm:^8.8.0":
"ws@npm:*, ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.16.0, ws@npm:^8.17.1, ws@npm:^8.18.0, ws@npm:^8.2.3, ws@npm:^8.8.0":
version: 8.18.1
resolution: "ws@npm:8.18.1"
peerDependencies: