Skip to content

Commit e009e32

Browse files
authored
Dialog Focus Fix (#1235)
1 parent 0d26d2b commit e009e32

File tree

3 files changed

+39
-4
lines changed

3 files changed

+39
-4
lines changed

.changeset/tasty-mangos-think.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/components": patch
3+
---
4+
5+
Dialog properly auto-focuses cancel button by default when originating from a FocusZone/FocusTrap.

src/Dialog/Dialog.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {useCallback, useEffect, useRef} from 'react'
1+
import React, {useCallback, useEffect, useRef, useState} from 'react'
22
import styled from 'styled-components'
33
import Button, {ButtonPrimary, ButtonDanger, ButtonProps} from '../Button'
44
import Flex from '../Flex'
@@ -341,11 +341,16 @@ const buttonTypes = {
341341
const Buttons: React.FC<{buttons: DialogButtonProps[]}> = ({buttons}) => {
342342
const autoFocusRef = useRef<HTMLButtonElement>(null)
343343
let autoFocusCount = 0
344+
const [hasRendered, setHasRendered] = useState(0)
344345
useEffect(() => {
345-
if (autoFocusRef.current) {
346-
autoFocusRef.current.focus()
346+
// hack to work around dialogs originating from other focus traps.
347+
if (hasRendered === 1) {
348+
autoFocusRef.current?.focus()
349+
} else {
350+
setHasRendered(hasRendered + 1)
347351
}
348-
}, [])
352+
}, [hasRendered])
353+
349354
return (
350355
<>
351356
{buttons.map((dialogButtonProps, index) => {

src/stories/ConfirmationDialog.stories.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {Meta} from '@storybook/react'
33

44
import {BaseStyles, Button, Flex, ThemeProvider, useTheme} from '..'
55
import {ConfirmationDialog, useConfirm} from '../Dialog/ConfirmationDialog'
6+
import {ActionMenu} from '../ActionMenu'
67

78
export default {
89
title: 'Internal components/ConfirmationDialog',
@@ -78,3 +79,27 @@ export const ShorthandHook = () => {
7879
</Flex>
7980
)
8081
}
82+
83+
export const ShorthandHookFromActionMenu = () => {
84+
const confirm = useConfirm()
85+
const [text, setText] = useState('open me')
86+
const onButtonClick = useCallback(async () => {
87+
if (await confirm({title: 'Are you sure?', content: 'Do you really want to do a trick?'})) {
88+
setText('tada!')
89+
}
90+
}, [confirm])
91+
92+
return (
93+
<Flex flexDirection="column" alignItems="flex-start">
94+
<ActionMenu
95+
renderAnchor={props => <Button {...props}>{text}</Button>}
96+
items={[
97+
{
98+
text: 'Do a trick!',
99+
onAction: onButtonClick
100+
}
101+
]}
102+
/>
103+
</Flex>
104+
)
105+
}

0 commit comments

Comments
 (0)