Skip to content

Commit

Permalink
Port "Ensure correct DOM node order when performing focus actions"
Browse files Browse the repository at this point in the history
tailwindlabs/headlessui#1038

This test didn't actually fail for me but w/e.
  • Loading branch information
rgossiaux committed Jun 11, 2023
1 parent 746c91e commit 2134daf
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 11 deletions.
39 changes: 39 additions & 0 deletions src/lib/components/tabs/tabs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,45 @@ describe('Rendering', () => {
assertTabs({ active: 0 })
})

it('should guarantee the order of DOM nodes when performing actions', async () => {
render(svelte`
<script>
let hide = false;
</script>
<button on:click={() => hide = !hide}>toggle</button>
<TabGroup>
<TabList>
<Tab>Tab 1</Tab>
{#if !hide}
<Tab>Tab 2</Tab>
{/if}
<Tab>Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel>Content 1</TabPanel>
{#if !hide}
<TabPanel>Content 2</TabPanel>
{/if}
<TabPanel>Content 3</TabPanel>
</TabPanels>
</TabGroup>
`)

await click(getByText('toggle')) // Remove Tab 2
await click(getByText('toggle')) // Re-add Tab 2

await press(Keys.Tab)
assertTabs({ active: 0 })

await press(Keys.ArrowRight)
assertTabs({ active: 1 })

await press(Keys.ArrowRight)
assertTabs({ active: 2 })
})

describe('`slot props`', () => {
it('should expose the `selectedIndex` on the `TabGroup` component', async () => {
render(svelte`
Expand Down
8 changes: 2 additions & 6 deletions src/lib/test-utils/accessibility-assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1359,12 +1359,8 @@ export function assertTabs(
expect(list).toHaveAttribute("role", "tablist");
expect(list).toHaveAttribute("aria-orientation", orientation);

let activeTab = tabs.find(
(tab) => tab.dataset.headlessuiIndex === "" + active
);
let activePanel = panels.find(
(panel) => panel.dataset.headlessuiIndex === "" + active
);
let activeTab = Array.from(list.querySelectorAll('[id^="headlessui-tabs-tab-"]'))[active]
let activePanel = panels.find(panel => panel.id === activeTab.getAttribute('aria-controls'))

for (let tab of tabs) {
expect(tab).toHaveAttribute("id");
Expand Down
16 changes: 11 additions & 5 deletions src/lib/utils/focus-management.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ let focusableSelector = [
.map(
process.env.NODE_ENV === "test"
? // TODO: Remove this once JSDOM fixes the issue where an element that is
// "hidden" can be the document.activeElement, because this is not possible
// in real browsers.
(selector) =>
`${selector}:not([tabindex='-1']):not([style*='display: none'])`
// "hidden" can be the document.activeElement, because this is not possible
// in real browsers.
(selector) =>
`${selector}:not([tabindex='-1']):not([style*='display: none'])`
: (selector) => `${selector}:not([tabindex='-1'])`
)
.join(",");
Expand Down Expand Up @@ -100,7 +100,13 @@ export function focusElement(element: HTMLElement | null) {

export function focusIn(container: HTMLElement | HTMLElement[], focus: Focus) {
let elements = Array.isArray(container)
? container
? container.slice().sort((a, b) => {
let position = a.compareDocumentPosition(b)

if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1
if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1
return 0
})
: getFocusableElements(container);
let active = document.activeElement as HTMLElement;

Expand Down

0 comments on commit 2134daf

Please sign in to comment.