Skip to content

Commit 7e5c213

Browse files
authored
fix(Dialog): focus does not switch to footer (#2532)
1 parent 881a660 commit 7e5c213

File tree

3 files changed

+46
-3
lines changed

3 files changed

+46
-3
lines changed

.changeset/nice-groups-cry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@frontify/fondue-components": minor
3+
---
4+
5+
fix(Dialog): focus does not switch to footer

packages/components/src/components/Dialog/Dialog.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,11 +217,18 @@ export const DialogContent = (
217217
const handleOpenAutoFocus = (event: Event) => {
218218
event.preventDefault();
219219

220+
const focusableSelector =
221+
'button:not([data-tooltip-trigger="true"]), [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
222+
223+
// Try to find focusable element in order: body -> footer -> header
220224
const dialogBody = contentRef.current?.querySelector('[data-dialog-layout-component="body"]');
225+
const dialogFooter = contentRef.current?.querySelector('[data-dialog-layout-component="footer"]');
226+
const dialogHeader = contentRef.current?.querySelector('[data-dialog-layout-component="header"]');
221227

222-
const firstFocusable = dialogBody?.querySelector(
223-
'button:not([data-tooltip-trigger="true"]), [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
224-
);
228+
const firstFocusable =
229+
dialogBody?.querySelector(focusableSelector) ||
230+
dialogFooter?.querySelector(focusableSelector) ||
231+
dialogHeader?.querySelector(focusableSelector);
225232

226233
if (firstFocusable instanceof HTMLElement) {
227234
firstFocusable.focus();

packages/components/src/components/Dialog/__tests__/Dialog.ct.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,3 +530,34 @@ test('should not focus the tooltip trigger when dialog opens', async ({ mount, p
530530
await expect(page.getByTestId('tooltip-trigger')).not.toBeFocused();
531531
await expect(page.getByTestId(TEXT_INPUT_TEST_ID_1)).toBeFocused();
532532
});
533+
534+
test('should focus first button in footer when body has no focusable elements', async ({ mount, page }) => {
535+
const component = await mount(
536+
<Dialog.Root>
537+
<Dialog.Trigger>
538+
<Button>{DIALOG_TRIGGER_TEXT}</Button>
539+
</Dialog.Trigger>
540+
<Dialog.Content>
541+
<Dialog.Header>{DIALOG_HEADER_TEXT}</Dialog.Header>
542+
<Dialog.Body>
543+
<p>Just some text, no focusable elements</p>
544+
</Dialog.Body>
545+
<Dialog.Footer>
546+
<Button data-test-id="footer-button-1">Cancel</Button>
547+
<Button data-test-id="footer-button-2">Submit</Button>
548+
</Dialog.Footer>
549+
</Dialog.Content>
550+
</Dialog.Root>,
551+
);
552+
553+
const dialogTrigger = page.getByTestId(DIALOG_TRIGGER_TEST_ID);
554+
555+
await expect(component).toBeVisible();
556+
await expect(dialogTrigger).toBeVisible();
557+
558+
await dialogTrigger.focus();
559+
await page.keyboard.press('Enter');
560+
561+
const firstFooterButton = page.getByTestId('footer-button-1');
562+
await expect(firstFooterButton).toBeFocused();
563+
});

0 commit comments

Comments
 (0)