Skip to content

Commit

Permalink
feat: Support fragments in collections (#6430)
Browse files Browse the repository at this point in the history
Co-authored-by: Reid Barber <reid@reidbarber.com>
Co-authored-by: Robert Snow <rsnow@adobe.com>
  • Loading branch information
3 people authored Aug 5, 2024
1 parent 4969019 commit 455cbf4
Show file tree
Hide file tree
Showing 2 changed files with 408 additions and 2 deletions.
382 changes: 382 additions & 0 deletions packages/@react-spectrum/tabs/test/Tabs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -890,4 +890,386 @@ describe('Tabs', function () {
fireEvent.keyDown(tabs[1], {key: 'ArrowRight'});
expect(tabs[2]).toHaveAttribute('aria-selected', 'true');
});

describe('when using fragments', function () {
it('renders fragment with children properly', function () {
let container = render(
<Provider theme={theme}>
<Tabs aria-label="Tab Example" maxWidth={500}>
<TabList>
<>
<Item>Tab 1</Item>
<Item>Tab 2</Item>
</>
</TabList>
<TabPanels>
<>
<Item>
Tab 1 content
</Item>
<Item>
Tab 2 content
</Item>
</>
</TabPanels>
</Tabs>
</Provider>
);

let tablist = container.getByRole('tablist');
expect(tablist).toBeTruthy();

expect(tablist).toHaveAttribute('aria-orientation', 'horizontal');

let tabs = within(tablist).getAllByRole('tab');
expect(tabs.length).toBe(2);

for (let tab of tabs) {
expect(tab).toHaveAttribute('tabindex');
expect(tab).toHaveAttribute('aria-selected');
let isSelected = tab.getAttribute('aria-selected') === 'true';
if (isSelected) {
expect(tab).toHaveAttribute('aria-controls');
let tabpanel = document.getElementById(tab.getAttribute('aria-controls'));
expect(tabpanel).toBeTruthy();
expect(tabpanel).toHaveAttribute('aria-labelledby', tab.id);
expect(tabpanel).toHaveAttribute('role', 'tabpanel');
expect(tabpanel).toHaveTextContent('Tab 1 content');
}
}
});

it('renders beginning fragment sibling properly', function () {
let container = render(
<Provider theme={theme}>
<Tabs aria-label="Tab Example" maxWidth={500}>
<TabList>
<>
<Item>Tab 1</Item>
</>
<Item>Tab 2</Item>
</TabList>
<TabPanels>
<>
<Item>
Tab 1 content
</Item>
</>
<Item>
Tab 2 content
</Item>
</TabPanels>
</Tabs>
</Provider>
);

let tablist = container.getByRole('tablist');
expect(tablist).toBeTruthy();

expect(tablist).toHaveAttribute('aria-orientation', 'horizontal');

let tabs = within(tablist).getAllByRole('tab');
expect(tabs.length).toBe(2);

for (let tab of tabs) {
expect(tab).toHaveAttribute('tabindex');
expect(tab).toHaveAttribute('aria-selected');
let isSelected = tab.getAttribute('aria-selected') === 'true';
if (isSelected) {
expect(tab).toHaveAttribute('aria-controls');
let tabpanel = document.getElementById(tab.getAttribute('aria-controls'));
expect(tabpanel).toBeTruthy();
expect(tabpanel).toHaveAttribute('aria-labelledby', tab.id);
expect(tabpanel).toHaveAttribute('role', 'tabpanel');
expect(tabpanel).toHaveTextContent('Tab 1 content');
}
}
});

it('renders middle fragment sibling properly', function () {
let container = render(
<Provider theme={theme}>
<Tabs aria-label="Tab Example" maxWidth={500}>
<TabList>
<Item>Tab 1</Item>
<>
<Item>Tab 2</Item>
</>
<Item>Tab 3</Item>
</TabList>
<TabPanels>
<Item>
Tab 1 content
</Item>
<>
<Item>
Tab 2 content
</Item>
</>
<Item>
Tab 3 content
</Item>
</TabPanels>
</Tabs>
</Provider>
);

let tablist = container.getByRole('tablist');
expect(tablist).toBeTruthy();

expect(tablist).toHaveAttribute('aria-orientation', 'horizontal');

let tabs = within(tablist).getAllByRole('tab');
expect(tabs.length).toBe(3);

for (let tab of tabs) {
expect(tab).toHaveAttribute('tabindex');
expect(tab).toHaveAttribute('aria-selected');
let isSelected = tab.getAttribute('aria-selected') === 'true';
if (isSelected) {
expect(tab).toHaveAttribute('aria-controls');
let tabpanel = document.getElementById(tab.getAttribute('aria-controls'));
expect(tabpanel).toBeTruthy();
expect(tabpanel).toHaveAttribute('aria-labelledby', tab.id);
expect(tabpanel).toHaveAttribute('role', 'tabpanel');
expect(tabpanel).toHaveTextContent('Tab 1 content');
}
}
});

it('renders ending fragment sibling properly', function () {
let container = render(
<Provider theme={theme}>
<Tabs aria-label="Tab Example" maxWidth={500}>
<TabList>
<Item>Tab 1</Item>
<>
<Item>Tab 2</Item>
</>
</TabList>
<TabPanels>
<Item>
Tab 1 content
</Item>
<>
<Item>
Tab 2 content
</Item>
</>
</TabPanels>
</Tabs>
</Provider>
);

let tablist = container.getByRole('tablist');
expect(tablist).toBeTruthy();

expect(tablist).toHaveAttribute('aria-orientation', 'horizontal');

let tabs = within(tablist).getAllByRole('tab');
expect(tabs.length).toBe(2);

for (let tab of tabs) {
expect(tab).toHaveAttribute('tabindex');
expect(tab).toHaveAttribute('aria-selected');
let isSelected = tab.getAttribute('aria-selected') === 'true';
if (isSelected) {
expect(tab).toHaveAttribute('aria-controls');
let tabpanel = document.getElementById(tab.getAttribute('aria-controls'));
expect(tabpanel).toBeTruthy();
expect(tabpanel).toHaveAttribute('aria-labelledby', tab.id);
expect(tabpanel).toHaveAttribute('role', 'tabpanel');
expect(tabpanel).toHaveTextContent('Tab 1 content');
}
}
});

it('renders list and panel fragment siblings in non-matching positions properly, list fragment first', function () {
let container = render(
<Provider theme={theme}>
<Tabs aria-label="Tab Example" maxWidth={500}>
<TabList>
<>
<Item>Tab 1</Item>
</>
<Item>Tab 2</Item>
</TabList>
<TabPanels>
<Item>
Tab 1 content
</Item>
<>
<Item>
Tab 2 content
</Item>
</>
</TabPanels>
</Tabs>
</Provider>
);

let tablist = container.getByRole('tablist');
expect(tablist).toBeTruthy();

expect(tablist).toHaveAttribute('aria-orientation', 'horizontal');

let tabs = within(tablist).getAllByRole('tab');
expect(tabs.length).toBe(2);

for (let tab of tabs) {
expect(tab).toHaveAttribute('tabindex');
expect(tab).toHaveAttribute('aria-selected');
let isSelected = tab.getAttribute('aria-selected') === 'true';
if (isSelected) {
expect(tab).toHaveAttribute('aria-controls');
let tabpanel = document.getElementById(tab.getAttribute('aria-controls'));
expect(tabpanel).toBeTruthy();
expect(tabpanel).toHaveAttribute('aria-labelledby', tab.id);
expect(tabpanel).toHaveAttribute('role', 'tabpanel');
expect(tabpanel).toHaveTextContent('Tab 1 content');
}
}
});

it('renders list and panel fragment siblings in non-matching positions properly, panel fragment first', function () {
let container = render(
<Provider theme={theme}>
<Tabs aria-label="Tab Example" maxWidth={500}>
<TabList>
<Item>Tab 1</Item>
<>
<Item>Tab 2</Item>
</>
</TabList>
<TabPanels>
<>
<Item>
Tab 1 content
</Item>
</>
<Item>
Tab 2 content
</Item>
</TabPanels>
</Tabs>
</Provider>
);

let tablist = container.getByRole('tablist');
expect(tablist).toBeTruthy();

expect(tablist).toHaveAttribute('aria-orientation', 'horizontal');

let tabs = within(tablist).getAllByRole('tab');
expect(tabs.length).toBe(2);

for (let tab of tabs) {
expect(tab).toHaveAttribute('tabindex');
expect(tab).toHaveAttribute('aria-selected');
let isSelected = tab.getAttribute('aria-selected') === 'true';
if (isSelected) {
expect(tab).toHaveAttribute('aria-controls');
let tabpanel = document.getElementById(tab.getAttribute('aria-controls'));
expect(tabpanel).toBeTruthy();
expect(tabpanel).toHaveAttribute('aria-labelledby', tab.id);
expect(tabpanel).toHaveAttribute('role', 'tabpanel');
expect(tabpanel).toHaveTextContent('Tab 1 content');
}
}
});

it('renders fragment with renderer properly', function () {
let container = render(
<Provider theme={theme}>
<Tabs aria-label="Tab Sample" items={defaultItems}>
<TabList>
<>
{item => (
<Item key={item.name} title={item.name || item.children} />
)}
</>
</TabList>
<TabPanels>
<>
{item => (
<Item key={item.name} title={item.name}>
{item.children}
</Item>
)}
</>
</TabPanels>
</Tabs>
</Provider>
);

let tablist = container.getByRole('tablist');
expect(tablist).toBeTruthy();

expect(tablist).toHaveAttribute('aria-orientation', 'horizontal');

let tabs = within(tablist).getAllByRole('tab');
expect(tabs.length).toBe(3);

for (let tab of tabs) {
expect(tab).toHaveAttribute('tabindex');
expect(tab).toHaveAttribute('aria-selected');
let isSelected = tab.getAttribute('aria-selected') === 'true';
if (isSelected) {
expect(tab).toHaveAttribute('aria-controls');
let tabpanel = document.getElementById(tab.getAttribute('aria-controls'));
expect(tabpanel).toBeTruthy();
expect(tabpanel).toHaveAttribute('aria-labelledby', tab.id);
expect(tabpanel).toHaveAttribute('role', 'tabpanel');
expect(tabpanel).toHaveTextContent(defaultItems[0].children);
}
}
});

it('renders fragment with mapper properly', function () {
let container = render(
<Provider theme={theme}>
<Tabs aria-label="Tab Sample">
<TabList>
<>
{defaultItems.map(item => (
<Item key={item.name} title={item.name || item.children} />
))}
</>
</TabList>
<TabPanels>
<>
{defaultItems.map(item => (
<Item key={item.name}>
{item.children}
</Item>
))}
</>
</TabPanels>
</Tabs>
</Provider>
);

let tablist = container.getByRole('tablist');
expect(tablist).toBeTruthy();

expect(tablist).toHaveAttribute('aria-orientation', 'horizontal');

let tabs = within(tablist).getAllByRole('tab');
expect(tabs.length).toBe(3);

for (let tab of tabs) {
expect(tab).toHaveAttribute('tabindex');
expect(tab).toHaveAttribute('aria-selected');
let isSelected = tab.getAttribute('aria-selected') === 'true';
if (isSelected) {
expect(tab).toHaveAttribute('aria-controls');
let tabpanel = document.getElementById(tab.getAttribute('aria-controls'));
expect(tabpanel).toBeTruthy();
expect(tabpanel).toHaveAttribute('aria-labelledby', tab.id);
expect(tabpanel).toHaveAttribute('role', 'tabpanel');
expect(tabpanel).toHaveTextContent(defaultItems[0].children);
}
}
});
});
});
Loading

1 comment on commit 455cbf4

@rspbot
Copy link

@rspbot rspbot commented on 455cbf4 Aug 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.