Repository: adobe/react-spectrum
File: packages/@react-spectrum/s2/test/TableView.test.tsx (TypeScript)

Code before:
      );
    }

    let {getAllByRole, getByRole} = render(<Example />);
    await user.click(document.body);

    let cells = getAllByRole('gridcell');
    let afterButton = getByRole('button', {name: 'After'});
    await user.click(cells[3]); // Bar 2
    expect(document.activeElement).toBe(cells[3]);

    await user.click(afterButton);
    await user.tab({shift: true});
    await user.tab({shift: true});
    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">
        <CollectionRendererContext.Provider value={DefaultCollectionRenderer}>
          <TabList>
            {tabs.map((tab) => (
              <Tab key={tab.id} id={tab.id}>
                {tab.label}
              </Tab>
            ))}
          </TabList>
        </CollectionRendererContext.Provider>
      </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>
      );

Review comment (bug):
We'll want to delete this line, which should cause the test to fail.
Once that's verified, we'll want to add this line around the `renderEmptyState` call so that the rendered child doesn't pick up the wrong context https://github.com/adobe/react-spectrum/blob/5b4cb59a80023399c3a3f6340860a681c3b5b928/packages/%40react-spectrum/s2/src/TableView.tsx#L441

Code after:
    function Example() {
      let [disabledBehavior, setDisabledBehavior] = useState<DisabledBehavior>('selection');
      return (
        <>
          <button>Before</button>
          <TableView aria-label="Dynamic table" disabledBehavior={disabledBehavior} disabledKeys={['2']}>
            <TableHeader columns={columns}>
              {(column) => <Column {...column}>{column.name}</Column>}
            </TableHeader>
            <TableBody>
              <Row id="1"><Cell>Foo 1</Cell><Cell>Bar 1</Cell><Cell>Baz 1</Cell><Cell>Yah 1</Cell></Row>
              <Row id="2"><Cell>Foo 2</Cell><Cell>Bar 2</Cell><Cell>Baz 2</Cell><Cell>Yah 2</Cell></Row>
              <Row id="3"><Cell>Foo 3</Cell><Cell>Bar 3</Cell><Cell>Baz 3</Cell><Cell>Yah 3</Cell></Row>
            </TableBody>
          </TableView>
          <button onClick={() => setDisabledBehavior('all')}>After</button>
        </>
      );
    }

    let {getAllByRole, getByRole} = render(<Example />);
    await user.click(document.body);

    let cells = getAllByRole('gridcell');
    let afterButton = getByRole('button', {name: 'After'});
    await user.click(cells[3]); // Bar 2
    expect(document.activeElement).toBe(cells[3]);

    await user.click(afterButton);
    await user.tab({shift: true});
    await user.tab({shift: true});
    await user.tab();
    expect(document.activeElement).toBe(cells[3]);
  });

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