Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/@react-spectrum/s2/src/TableView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ import {
ButtonContext,
CellRenderProps,
Collection,
CollectionRendererContext,
ColumnRenderProps,
ColumnResizer,
ContextValue,
DEFAULT_SLOT,
DefaultCollectionRenderer,
Form,
Key,
OverlayTriggerStateContext,
Expand Down Expand Up @@ -439,7 +441,9 @@ export const TableBody = /*#__PURE__*/ (forwardRef as forwardRefType)(function T
if (renderEmptyState != null && !isLoading) {
emptyRender = (props: TableBodyRenderProps) => (
<div className={centeredWrapper}>
{renderEmptyState(props)}
<CollectionRendererContext.Provider value={DefaultCollectionRenderer}>
{renderEmptyState(props)}
</CollectionRendererContext.Provider>
</div>
);
} else if (loadingState === 'loading') {
Expand Down
59 changes: 58 additions & 1 deletion packages/@react-spectrum/s2/stories/TableView.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,24 @@ import {
PickerItem,
Row,
StatusLight,
Tab,
TableBody,
TableHeader,
TableView,
TableViewProps,
TabList,
Tabs,
Text,
TextField
} from '../src';
import {categorizeArgTypes, getActionArgs} from './utils';
import {CollectionRendererContext, DefaultCollectionRenderer, SortDescriptor} from 'react-aria-components';
import Edit from '../s2wf-icons/S2_Icon_Edit_20_N.svg';
import Filter from '../s2wf-icons/S2_Icon_Filter_20_N.svg';
import FolderOpen from '../spectrum-illustrations/linear/FolderOpen';
import {Key} from '@react-types/shared';
import type {Meta, StoryObj} from '@storybook/react';
import React, {ReactElement, useCallback, useEffect, useRef, useState} from 'react';
import {SortDescriptor} from 'react-aria-components';
import {style} from '../style/spectrum-theme' with {type: 'macro'};
import {useAsyncList, useListData} from '@react-stately/data';
import {useEffectEvent} from '@react-aria/utils';
Expand Down Expand Up @@ -1714,3 +1717,57 @@ export const EditableTableWithAsyncSaving: StoryObj<EditableTableProps> = {
);
}
};

export const TableViewCollectionError: StoryObj<typeof DynamicTable> = {
render: function DynamicColumnsExample() {
const columns: Array<{
key: string,
label: string,
align?: 'start' | 'center' | 'end'
}> = [
{key: 'name', label: 'Name'},
{key: 'count', label: 'Count', align: 'end'}
];

const tabs = [
{id: 'general', label: 'General'},
{id: 'advanced', label: 'Advanced'},
{id: 'about', label: 'About'}
];
const renderEmptyState = () => (
<CollectionRendererContext.Provider value={DefaultCollectionRenderer}>
{/* <Button>Sanity Check</Button> */}
<Tabs aria-label="Settings sections">
<TabList>
{tabs.map((tab) => (
<Tab key={tab.id} id={tab.id}>
{tab.label}
</Tab>
))}
</TabList>
</Tabs>
</CollectionRendererContext.Provider>
);

return (
<Content>
<TableView aria-label="Demo table" selectionMode="none" styles={style({height: 320, minHeight: 240, minWidth: 320})}>
<TableHeader>
{columns.map((column) => (
<Column key={column.key} align={column.align}>
{column.label}
</Column>
))}
</TableHeader>
<TableBody items={[]} renderEmptyState={renderEmptyState} />
</TableView>
</Content>
);
},
args: Example.args,
parameters: {
docs: {
disable: true
}
}
};
39 changes: 39 additions & 0 deletions packages/@react-spectrum/s2/test/TableView.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {DisabledBehavior} from '@react-types/shared';
import Filter from '../s2wf-icons/S2_Icon_Filter_20_N.svg';
import {pointerMap, User} from '@react-aria/test-utils';
import React, {useState} from 'react';
import {Tab, TabList, Tabs} from 'react-aria-components';
import userEvent from '@testing-library/user-event';

// @ts-ignore
Expand Down Expand Up @@ -148,4 +149,42 @@ describe('TableView', () => {
await user.tab();
expect(document.activeElement).toBe(cells[3]);
});

it('should render empty state + nested collection without crashing', async () => {
const tabs = [
{id: 'general', label: 'General'},
{id: 'advanced', label: 'Advanced'}
];
const renderEmptyState = () => (
<Tabs aria-label="Settings">
<TabList>
{tabs.map((tab) => (
<Tab key={tab.id} id={tab.id}>
{tab.label}
</Tab>
))}
</TabList>
</Tabs>
);

let renderResult: ReturnType<typeof render>;
await act(async () => {
renderResult = render(
<TableView aria-label="Debug table" selectionMode="none">
<TableHeader columns={columns}>
{(column) => (
<Column>
{column.name}
</Column>
)}
</TableHeader>
<TableBody items={[]} renderEmptyState={renderEmptyState} />
</TableView>
);
await Promise.resolve();
});
let {getAllByRole} = renderResult!;

expect(getAllByRole('tab')).toHaveLength(tabs.length);
});
});