Skip to content

Commit 71bdfa8

Browse files
Add support for initial focus ref to Dialog 2 (#4729)
* Add support for initial focus ref to Dialog 2 * add test and docs * Create seven-phones-talk.md
1 parent ba253d7 commit 71bdfa8

File tree

6 files changed

+63
-3
lines changed

6 files changed

+63
-3
lines changed

.changeset/seven-phones-talk.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/react": minor
3+
---
4+
5+
Dialog2: Add support for "InitialFocusRef" that allows to specify an element that should receive focus when the dialog opens.

packages/react/src/Dialog/Dialog.docs.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@
5959
"name": "returnFocusRef",
6060
"type": "React.RefObject<HTMLElement>",
6161
"describedby": "Return focus to this element when the Dialog closes, instead of the element that had focus immediately before the Dialog opened"
62+
},
63+
{
64+
"name": "initialFocusRef",
65+
"type": "React.RefObject<HTMLElement>",
66+
"description": "Focus this element when the Dialog opens"
6267
}
6368
],
6469
"subcomponents": []

packages/react/src/Dialog/Dialog.features.stories.tsx

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, {useState, useRef, useCallback} from 'react'
2-
import {Box, TextInput, Text, Button} from '..'
2+
import {Box, TextInput, Text, Button, ActionList} from '..'
33
import type {DialogProps, DialogWidth, DialogHeight} from './Dialog'
44
import {Dialog} from './Dialog'
55

@@ -304,3 +304,29 @@ export const ReturnFocusRef = () => {
304304
</React.Suspense>
305305
)
306306
}
307+
308+
export const NewIssues = () => {
309+
const [isOpen, setIsOpen] = useState(false)
310+
const onDialogClose = useCallback(() => setIsOpen(false), [])
311+
const initialFocusRef = useRef(null)
312+
return (
313+
<>
314+
<Button onClick={() => setIsOpen(true)}>Show dialog</Button>
315+
{isOpen ? (
316+
<Dialog
317+
initialFocusRef={initialFocusRef}
318+
onClose={onDialogClose}
319+
title="New issue"
320+
renderBody={() => (
321+
<ActionList>
322+
<ActionList.LinkItem ref={initialFocusRef} href="https://github.com">
323+
Item 1
324+
</ActionList.LinkItem>
325+
<ActionList.LinkItem href="https://github.com">Link</ActionList.LinkItem>
326+
</ActionList>
327+
)}
328+
></Dialog>
329+
) : null}
330+
</>
331+
)
332+
}

packages/react/src/Dialog/Dialog.test.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,21 @@ describe('Dialog', () => {
209209
expect(getByRole('button', {name: 'return focus to (button 2)'})).toHaveFocus()
210210
})
211211
})
212+
213+
it('automatically focuses the element that is specified as initialFocusRef', () => {
214+
const initialFocusRef = React.createRef<HTMLAnchorElement>()
215+
const {getByRole} = render(
216+
<Dialog
217+
initialFocusRef={initialFocusRef}
218+
onClose={() => {}}
219+
title="New issue"
220+
renderBody={() => (
221+
<a ref={initialFocusRef} href="https://github.com">
222+
Item 1
223+
</a>
224+
)}
225+
></Dialog>,
226+
)
227+
228+
expect(getByRole('link')).toHaveFocus()
229+
})

packages/react/src/Dialog/Dialog.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ export interface DialogProps extends SxProp {
138138
* instead of the element that had focus immediately before the Dialog opened
139139
*/
140140
returnFocusRef?: React.RefObject<HTMLElement>
141+
142+
/**
143+
* The element to focus when the Dialog opens
144+
*/
145+
initialFocusRef?: React.RefObject<HTMLElement>
141146
}
142147

143148
/**
@@ -403,6 +408,7 @@ const _Dialog = React.forwardRef<HTMLDivElement, React.PropsWithChildren<DialogP
403408
footerButtons = [],
404409
position = defaultPosition,
405410
returnFocusRef,
411+
initialFocusRef,
406412
sx,
407413
} = props
408414
const dialogLabelId = useId()
@@ -429,7 +435,7 @@ const _Dialog = React.forwardRef<HTMLDivElement, React.PropsWithChildren<DialogP
429435

430436
useFocusTrap({
431437
containerRef: dialogRef,
432-
initialFocusRef: autoFocusedFooterButtonRef,
438+
initialFocusRef: initialFocusRef ?? autoFocusedFooterButtonRef,
433439
restoreFocusOnCleanUp: returnFocusRef?.current ? false : true,
434440
returnFocusRef,
435441
})

packages/react/src/__tests__/Dialog.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {render as HTMLRender, fireEvent} from '@testing-library/react'
44
import axe from 'axe-core'
55
import {behavesAsComponent, checkExports} from '../utils/testing'
66

7-
/* Dialog Version 2 */
7+
/* Dialog Version 1*/
88

99
const comp = (
1010
<Dialog isOpen onDismiss={() => null} aria-labelledby="header">

0 commit comments

Comments
 (0)