Explore RA

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-07-29 10:01:11 +01:00
parent c33c6936ed
commit b441d6c9ec
7 changed files with 262 additions and 0 deletions
@@ -0,0 +1,44 @@
/*
* 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 {
Column as ReactAriaColumn,
type ColumnProps,
} from 'react-aria-components';
import { Icon } from '../Icon';
export const Column = (
props: Omit<ColumnProps, 'children'> & { children?: React.ReactNode },
) => {
return (
<ReactAriaColumn {...props}>
{({ allowsSorting, sortDirection }) => (
<div className="column-header">
{props.children}
{allowsSorting && (
<span aria-hidden="true" className="sort-indicator">
{sortDirection === 'ascending' ? (
<Icon name="arrow-up" size={16} />
) : (
<Icon name="arrow-down" size={16} />
)}
</span>
)}
</div>
)}
</ReactAriaColumn>
);
};
@@ -0,0 +1,92 @@
/*
* 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 { Table, TableHeader, Column, TableBody } from '.';
import { MemoryRouter } from 'react-router-dom';
import {
Table as ReactAriaTable,
TableHeader as ReactAriaTableHeader,
Column as ReactAriaColumn,
Cell,
Row as ReactAriaRow,
Cell as ReactAriaCell,
Row,
Checkbox,
} from 'react-aria-components';
const meta = {
title: 'Components/TableRA',
decorators: [
(Story: StoryFn) => (
<MemoryRouter>
<Story />
</MemoryRouter>
),
],
} satisfies Meta;
export default meta;
type Story = StoryObj<typeof meta>;
export const Uncontrolled: Story = {
render: () => {
return (
<Table>
<TableHeader>
<Column isRowHeader>Name</Column>
<Column>Type</Column>
<Column>Size</Column>
<Column>Date Modified</Column>
</TableHeader>
<TableBody>
<ReactAriaRow>
<ReactAriaCell>
<Checkbox slot="selection" />
</ReactAriaCell>
<ReactAriaCell>Games</ReactAriaCell>
<ReactAriaCell>File folder</ReactAriaCell>
<ReactAriaCell>6/7/2020</ReactAriaCell>
</ReactAriaRow>
<ReactAriaRow>
<ReactAriaCell>
<Checkbox slot="selection" />
</ReactAriaCell>
<ReactAriaCell>Program Files</ReactAriaCell>
<ReactAriaCell>File folder</ReactAriaCell>
<ReactAriaCell>4/7/2021</ReactAriaCell>
</ReactAriaRow>
<ReactAriaRow>
<ReactAriaCell>
<Checkbox slot="selection" />
</ReactAriaCell>
<ReactAriaCell>bootmgr</ReactAriaCell>
<ReactAriaCell>System file</ReactAriaCell>
<ReactAriaCell>11/20/2010</ReactAriaCell>
</ReactAriaRow>
<ReactAriaRow>
<ReactAriaCell>
<Checkbox slot="selection" />
</ReactAriaCell>
<ReactAriaCell>log.txt</ReactAriaCell>
<ReactAriaCell>Text Document</ReactAriaCell>
<ReactAriaCell>1/18/2016</ReactAriaCell>
</ReactAriaRow>
</TableBody>
</Table>
);
},
};
@@ -0,0 +1,24 @@
/*
* 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 as ReactAriaTable,
type TableProps,
} from 'react-aria-components';
export const Table = (props: TableProps) => {
return <ReactAriaTable {...props} />;
};
@@ -0,0 +1,24 @@
/*
* 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 {
TableBody as ReactAriaTableBody,
type TableBodyProps,
} from 'react-aria-components';
export const TableBody = <T extends object>(props: TableBodyProps<T>) => {
return <ReactAriaTableBody {...props} />;
};
@@ -0,0 +1,43 @@
/*
* 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 {
TableHeader as ReactAriaTableHeader,
type TableHeaderProps,
Checkbox,
} from 'react-aria-components';
import { Collection, useTableOptions } from 'react-aria-components';
import { Column } from './Column';
export const TableHeader = <T extends object>({
columns,
children,
}: TableHeaderProps<T>) => {
let { selectionBehavior, selectionMode, allowsDragging } = useTableOptions();
return (
<ReactAriaTableHeader>
{/* Add extra columns for drag and drop and selection. */}
{allowsDragging && <Column />}
{selectionBehavior === 'toggle' && (
<Column>
{selectionMode === 'multiple' && <Checkbox slot="selection" />}
</Column>
)}
<Collection items={columns}>{children}</Collection>
</ReactAriaTableHeader>
);
};
@@ -0,0 +1,20 @@
/*
* 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 { Table } from './Table';
export { TableHeader } from './TableHeader';
export { TableBody } from './TableBody';
export { Column } from './Column';
@@ -0,0 +1,15 @@
/*
* 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.
*/