diff --git a/.changeset/wet-terms-argue.md b/.changeset/wet-terms-argue.md new file mode 100644 index 00000000000..bf655004a8c --- /dev/null +++ b/.changeset/wet-terms-argue.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +Allow changing initially focused button in ConfirmationDialog diff --git a/packages/react/src/ConfirmationDialog/ConfirmationDialog.docs.json b/packages/react/src/ConfirmationDialog/ConfirmationDialog.docs.json index 60c3891d184..4c7dbec7c6d 100644 --- a/packages/react/src/ConfirmationDialog/ConfirmationDialog.docs.json +++ b/packages/react/src/ConfirmationDialog/ConfirmationDialog.docs.json @@ -37,6 +37,11 @@ "defaultValue": "normal", "description": "The type of button to use for the confirm button." }, + { + "name": "overrideButtonFocus", + "type": "'confirm' | 'cancel'", + "description": "The button that should be initially focused when the dialog is opened. By default, the initial button focus is the confirm button, unless the confirm button is dangerous, in which case the cancel button is focused. This prop should be used rarely, as it can allow dangerous actions to be taken accidentally." + }, { "name": "className", "type": "string", diff --git a/packages/react/src/ConfirmationDialog/ConfirmationDialog.test.tsx b/packages/react/src/ConfirmationDialog/ConfirmationDialog.test.tsx index f9ae828b448..888bf09406a 100644 --- a/packages/react/src/ConfirmationDialog/ConfirmationDialog.test.tsx +++ b/packages/react/src/ConfirmationDialog/ConfirmationDialog.test.tsx @@ -11,7 +11,10 @@ import theme from '../theme' import {ThemeProvider} from '../ThemeProvider' import {Stack} from '../Stack' -const Basic = ({confirmButtonType}: Pick, 'confirmButtonType'>) => { +const Basic = ({ + confirmButtonType, + overrideButtonFocus, +}: Pick, 'confirmButtonType' | 'overrideButtonFocus'>) => { const [isOpen, setIsOpen] = useState(false) const buttonRef = useRef(null) const onDialogClose = useCallback(() => setIsOpen(false), []) @@ -28,6 +31,7 @@ const Basic = ({confirmButtonType}: Pick Lorem ipsum dolor sit Pippin good dog. @@ -187,6 +191,15 @@ describe('ConfirmationDialog', () => { expect(dialog.getAttribute('data-height')).toBe('small') }) + it('focuses the confirm button even when dangerous if initialButtonFocus is confirm', async () => { + const {getByText, getByRole} = render() + + fireEvent.click(getByText('Show dialog')) + + expect(getByRole('button', {name: 'Primary'})).toEqual(document.activeElement) + expect(getByRole('button', {name: 'Secondary'})).not.toEqual(document.activeElement) + }) + describe('loading states', () => { it('applies loading state to confirm button when confirmButtonLoading is true', async () => { const {getByText, getByRole} = render() diff --git a/packages/react/src/ConfirmationDialog/ConfirmationDialog.tsx b/packages/react/src/ConfirmationDialog/ConfirmationDialog.tsx index 6223dfebfbf..19152d10254 100644 --- a/packages/react/src/ConfirmationDialog/ConfirmationDialog.tsx +++ b/packages/react/src/ConfirmationDialog/ConfirmationDialog.tsx @@ -51,6 +51,13 @@ export interface ConfirmationDialogProps { */ confirmButtonLoading?: boolean + /** + * Overrides the button that should be initially focused when the dialog is opened. By default, the confirm button + * is focused initially unless it is a dangerous action, in which case the cancel button is focused. This should + * rarely be overridden, in order to ensure that the user does not accidentally confirm a dangerous action. + */ + overrideButtonFocus?: 'cancel' | 'confirm' + /** * Additional class names to apply to the dialog */ @@ -125,6 +132,7 @@ export const ConfirmationDialog: React.FC { @@ -134,17 +142,19 @@ export const ConfirmationDialog: React.FC