From 6556634d031bc4ae6b4cced0c0523a6ce7115818 Mon Sep 17 00:00:00 2001 From: John Costa Date: Mon, 13 Apr 2026 20:25:42 -0700 Subject: [PATCH 01/10] fix(dialog): add aria-describedby support for alertdialog role AlertDialog was rendering with role="alertdialog" but without aria-describedby, which the WAI-ARIA spec recommends for alertdialogs to reference the alert message content. Changes: - useDialog hook: generate a content ID via useSlotId when role is "alertdialog", return contentProps with the ID, and set aria-describedby on the dialog element - V3 Dialog: spread contentProps onto the content slot so the Content element receives the generated ID - RAC Dialog: destructure contentProps (available for user composition) The aria-describedby is only auto-wired for alertdialog role. Regular dialogs are unaffected. Users can override via the aria-describedby prop. Fixes #9916 --- .../react-spectrum/src/dialog/Dialog.tsx | 6 +-- .../test/dialog/AlertDialog.test.js | 17 +++++++ packages/react-aria-components/src/Dialog.tsx | 2 +- packages/react-aria/src/dialog/useDialog.ts | 12 ++++- .../react-aria/test/dialog/useDialog.test.js | 44 +++++++++++++++++++ 5 files changed, 76 insertions(+), 5 deletions(-) diff --git a/packages/@adobe/react-spectrum/src/dialog/Dialog.tsx b/packages/@adobe/react-spectrum/src/dialog/Dialog.tsx index f49c3343694..0f8575475cf 100644 --- a/packages/@adobe/react-spectrum/src/dialog/Dialog.tsx +++ b/packages/@adobe/react-spectrum/src/dialog/Dialog.tsx @@ -73,7 +73,7 @@ export const Dialog = React.forwardRef(function Dialog(props: SpectrumDialogProp let domRef = useDOMRef(ref); let gridRef = useRef(null); let sizeVariant = sizeMap[type] || sizeMap[size]; - let {dialogProps, titleProps} = useDialog(mergeProps(contextProps, props), domRef); + let {dialogProps, titleProps, contentProps} = useDialog(mergeProps(contextProps, props), domRef); let hasHeader = useHasChild(`.${styles['spectrum-Dialog-header']}`, unwrapDOMRef(gridRef)); let hasHeading = useHasChild(`.${styles['spectrum-Dialog-heading']}`, unwrapDOMRef(gridRef)); @@ -86,11 +86,11 @@ export const Dialog = React.forwardRef(function Dialog(props: SpectrumDialogProp header: {UNSAFE_className: classNames(styles, 'spectrum-Dialog-header', {'spectrum-Dialog-header--noHeading': !hasHeading, 'spectrum-Dialog-header--noTypeIcon': !hasTypeIcon})}, typeIcon: {UNSAFE_className: styles['spectrum-Dialog-typeIcon']}, divider: {UNSAFE_className: styles['spectrum-Dialog-divider'], size: 'M'}, - content: {UNSAFE_className: styles['spectrum-Dialog-content']}, + content: {UNSAFE_className: styles['spectrum-Dialog-content'], ...contentProps}, footer: {UNSAFE_className: styles['spectrum-Dialog-footer']}, buttonGroup: {UNSAFE_className: classNames(styles, 'spectrum-Dialog-buttonGroup', {'spectrum-Dialog-buttonGroup--noFooter': !hasFooter}), align: 'end'} // eslint-disable-next-line react-hooks/exhaustive-deps - }), [hasFooter, hasHeader, titleProps]); + }), [hasFooter, hasHeader, titleProps, contentProps]); return (
+ + Content body + + + ); + + let dialog = getByRole('alertdialog'); + expect(dialog).toHaveAttribute('aria-describedby'); + let contentId = dialog.getAttribute('aria-describedby'); + let content = document.getElementById(contentId); + expect(content).not.toBeNull(); + expect(content.textContent).toBe('Content body'); + }); }); diff --git a/packages/react-aria-components/src/Dialog.tsx b/packages/react-aria-components/src/Dialog.tsx index d3d2a8b4fcc..a9822fa451e 100644 --- a/packages/react-aria-components/src/Dialog.tsx +++ b/packages/react-aria-components/src/Dialog.tsx @@ -105,7 +105,7 @@ export function DialogTrigger(props: DialogTriggerProps): JSX.Element { export const Dialog = /*#__PURE__*/ (forwardRef as forwardRefType)(function Dialog(props: DialogProps, ref: ForwardedRef) { let originalAriaLabelledby = props['aria-labelledby']; [props, ref] = useContextProps(props, ref, DialogContext); - let {dialogProps, titleProps} = useDialog({ + let {dialogProps, titleProps, contentProps} = useDialog({ ...props, // Only pass aria-labelledby from props, not context. // Context is used as a fallback below. diff --git a/packages/react-aria/src/dialog/useDialog.ts b/packages/react-aria/src/dialog/useDialog.ts index 8a2015231fa..914fa3a1b69 100644 --- a/packages/react-aria/src/dialog/useDialog.ts +++ b/packages/react-aria/src/dialog/useDialog.ts @@ -31,7 +31,10 @@ export interface DialogAria { dialogProps: DOMAttributes, /** Props for the dialog title element. */ - titleProps: DOMAttributes + titleProps: DOMAttributes, + + /** Props for the dialog content/description element. Used for aria-describedby on alertdialogs. */ + contentProps: DOMAttributes } /** @@ -45,6 +48,9 @@ export function useDialog(props: AriaDialogProps, ref: RefObject +

Alert Title

+ {props.showContent &&

Alert message content

} + {props.children} + + ); + } + + it('should set aria-describedby on alertdialog when content is rendered', function () { + let res = render(); + let el = res.getByTestId('test'); + let contentEl = el.querySelector('p'); + expect(el).toHaveAttribute('aria-describedby', contentEl.id); + }); + + it('should not set aria-describedby on regular dialog', function () { + function RegularDialogExample(props) { + let ref = useRef(); + let {dialogProps, titleProps, contentProps} = useDialog(props, ref); + return ( +
+

Title

+

Content

+
+ ); + } + + let res = render(); + let el = res.getByTestId('test'); + expect(el).not.toHaveAttribute('aria-describedby'); + }); + + it('should allow aria-describedby override on alertdialog', function () { + let res = render(); + let el = res.getByTestId('test'); + expect(el).toHaveAttribute('aria-describedby', 'custom-id'); + }); + }); + describe('dev warnings', function () { let originalWarn; From afbfd15fa32c5861f5ee175fb1b274b69fecb941 Mon Sep 17 00:00:00 2001 From: John Costa Date: Mon, 13 Apr 2026 20:43:51 -0700 Subject: [PATCH 02/10] fix: wire contentProps via TextContext for RAC alertdialog aria-describedby --- packages/react-aria-components/src/Dialog.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/react-aria-components/src/Dialog.tsx b/packages/react-aria-components/src/Dialog.tsx index a9822fa451e..0862b71da77 100644 --- a/packages/react-aria-components/src/Dialog.tsx +++ b/packages/react-aria-components/src/Dialog.tsx @@ -16,6 +16,7 @@ import {ContextValue, DEFAULT_SLOT, dom, DOMRenderProps, Provider, SlotProps, St import {filterDOMProps} from 'react-aria/filterDOMProps'; import {forwardRefType, GlobalDOMAttributes} from '@react-types/shared'; import {HeadingContext} from './Heading'; +import {TextContext} from './Text'; import {mergeProps} from 'react-aria/mergeProps'; import {OverlayTriggerProps, OverlayTriggerState} from 'react-stately/useOverlayTriggerState'; import {PopoverContext} from './Popover'; @@ -149,6 +150,12 @@ export const Dialog = /*#__PURE__*/ (forwardRef as forwardRefType)(function Dial title: {...titleProps, level: 2} } }], + [TextContext, { + slots: { + [DEFAULT_SLOT]: {}, + description: contentProps + } + }], [ButtonContext, { slots: { [DEFAULT_SLOT]: {}, From eec13abd56002346afd97673be0674d18473aa6d Mon Sep 17 00:00:00 2001 From: John Costa Date: Mon, 13 Apr 2026 20:53:22 -0700 Subject: [PATCH 03/10] test: add RAC integration test for alertdialog aria-describedby via Text slot --- .../react-aria-components/test/Dialog.test.js | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/react-aria-components/test/Dialog.test.js b/packages/react-aria-components/test/Dialog.test.js index 69375f92fd3..42e0193a38f 100644 --- a/packages/react-aria-components/test/Dialog.test.js +++ b/packages/react-aria-components/test/Dialog.test.js @@ -23,6 +23,7 @@ import {OverlayArrow} from '../src/OverlayArrow'; import {Popover} from '../src/Popover'; import React, {useRef} from 'react'; import * as stories from '../stories/Modal.stories'; +import {Text} from '../src/Text'; import {TextField} from '../src/TextField'; import {UNSAFE_PortalProvider} from 'react-aria/PortalProvider'; import {User} from '@react-aria/test-utils'; @@ -75,6 +76,8 @@ describe('Dialog', () => { let heading = getByRole('heading'); expect(dialog).toHaveAttribute('aria-labelledby', heading.id); expect(dialog).toHaveAttribute('data-test', 'dialog'); + // aria-describedby is not set here because no Text slot="description" is rendered + expect(dialog).not.toHaveAttribute('aria-describedby'); expect(dialog.closest('.react-aria-Modal')).toHaveAttribute('data-test', 'modal'); expect(dialog.closest('.react-aria-ModalOverlay')).toBeInTheDocument(); @@ -85,6 +88,36 @@ describe('Dialog', () => { expect(dialog).not.toBeInTheDocument(); }); + it('should set aria-describedby when Text slot="description" is used in alertdialog', async () => { + let {getByRole} = render( + + + + + {({close}) => ( + <> + Alert Title + This is the alert message. + + + )} + + + + ); + + let button = getByRole('button'); + let dialogTester = testUtilUser.createTester('Dialog', {root: button, overlayType: 'modal'}); + await dialogTester.open(); + let dialog = dialogTester.dialog; + expect(dialog).toHaveAttribute('role', 'alertdialog'); + expect(dialog).toHaveAttribute('aria-describedby'); + let descId = dialog.getAttribute('aria-describedby'); + let descEl = document.getElementById(descId); + expect(descEl).not.toBeNull(); + expect(descEl.textContent).toBe('This is the alert message.'); + }); + it('works with modal and custom underlay', async () => { let {getByRole} = render( From 368799e4ad77ce457ef313cdc8a6e85bbc1b5f57 Mon Sep 17 00:00:00 2001 From: John Costa Date: Tue, 14 Apr 2026 13:46:17 -0700 Subject: [PATCH 04/10] fix: sort TextContext import alphabetically in RAC Dialog --- packages/react-aria-components/src/Dialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-aria-components/src/Dialog.tsx b/packages/react-aria-components/src/Dialog.tsx index 0862b71da77..d30407ab3af 100644 --- a/packages/react-aria-components/src/Dialog.tsx +++ b/packages/react-aria-components/src/Dialog.tsx @@ -16,13 +16,13 @@ import {ContextValue, DEFAULT_SLOT, dom, DOMRenderProps, Provider, SlotProps, St import {filterDOMProps} from 'react-aria/filterDOMProps'; import {forwardRefType, GlobalDOMAttributes} from '@react-types/shared'; import {HeadingContext} from './Heading'; -import {TextContext} from './Text'; import {mergeProps} from 'react-aria/mergeProps'; import {OverlayTriggerProps, OverlayTriggerState} from 'react-stately/useOverlayTriggerState'; import {PopoverContext} from './Popover'; import {PressResponder} from 'react-aria/private/interactions/PressResponder'; import React, {createContext, ForwardedRef, forwardRef, JSX, ReactNode, useCallback, useContext, useRef, useState} from 'react'; import {RootMenuTriggerStateContext} from './Menu'; +import {TextContext} from './Text'; import {useId} from 'react-aria/useId'; import {useMenuTriggerState} from 'react-stately/useMenuTriggerState'; import {useOverlayTrigger} from 'react-aria/useOverlayTrigger'; From cf8cdb009c4913caf85116d86dd620608ac0eac0 Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Wed, 22 Apr 2026 12:56:06 -0700 Subject: [PATCH 05/10] update tests, add warning to RAC --- packages/react-aria-components/src/Dialog.tsx | 6 ++++++ packages/react-aria-components/test/Dialog.test.js | 8 +++++--- packages/react-aria/test/dialog/useDialog.test.js | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/react-aria-components/src/Dialog.tsx b/packages/react-aria-components/src/Dialog.tsx index d30407ab3af..98ca95419aa 100644 --- a/packages/react-aria-components/src/Dialog.tsx +++ b/packages/react-aria-components/src/Dialog.tsx @@ -123,6 +123,12 @@ export const Dialog = /*#__PURE__*/ (forwardRef as forwardRefType)(function Dial console.warn('If a Dialog does not contain a , it must have an aria-label or aria-labelledby attribute for accessibility.'); } } + + if (!dialogProps['aria-describedby'] && dialogProps['role'] === 'alertdialog') { + if (process.env.NODE_ENV !== 'production') { + console.warn('If a Dialog does not contain a , it must have an aria-describedby for accessibility'); + } + } let renderProps = useRenderProps({ defaultClassName: 'react-aria-Dialog', diff --git a/packages/react-aria-components/test/Dialog.test.js b/packages/react-aria-components/test/Dialog.test.js index 42e0193a38f..563fbd589ab 100644 --- a/packages/react-aria-components/test/Dialog.test.js +++ b/packages/react-aria-components/test/Dialog.test.js @@ -60,6 +60,7 @@ describe('Dialog', () => { {({close}) => ( <> Alert + This is the alert message. )} @@ -76,9 +77,6 @@ describe('Dialog', () => { let heading = getByRole('heading'); expect(dialog).toHaveAttribute('aria-labelledby', heading.id); expect(dialog).toHaveAttribute('data-test', 'dialog'); - // aria-describedby is not set here because no Text slot="description" is rendered - expect(dialog).not.toHaveAttribute('aria-describedby'); - expect(dialog.closest('.react-aria-Modal')).toHaveAttribute('data-test', 'modal'); expect(dialog.closest('.react-aria-ModalOverlay')).toBeInTheDocument(); @@ -128,6 +126,7 @@ describe('Dialog', () => { {({close}) => ( <> Alert + This is the alert message. )} @@ -163,6 +162,7 @@ describe('Dialog', () => { {({close}) => ( <> Alert + This is the alert message. )} @@ -354,6 +354,7 @@ describe('Dialog', () => { {({close}) => ( <> Alert + This is the alert message. )} @@ -394,6 +395,7 @@ describe('Dialog', () => { {({close}) => ( <> Alert + This is the alert message. )} diff --git a/packages/react-aria/test/dialog/useDialog.test.js b/packages/react-aria/test/dialog/useDialog.test.js index 3b82b269301..b85332d1d83 100644 --- a/packages/react-aria/test/dialog/useDialog.test.js +++ b/packages/react-aria/test/dialog/useDialog.test.js @@ -75,7 +75,7 @@ describe('useDialog', function () { expect(el).toHaveAttribute('aria-describedby', contentEl.id); }); - it('should not set aria-describedby on regular dialog', function () { + it('should not auto-wire aria-describedby on regular dialog, but contentProps.id is still provided', function () { function RegularDialogExample(props) { let ref = useRef(); let {dialogProps, titleProps, contentProps} = useDialog(props, ref); From 4408961708583435de969eb151cc54b8d32f1626 Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Wed, 22 Apr 2026 13:27:55 -0700 Subject: [PATCH 06/10] add describedby to s2 alert dialog --- packages/@react-spectrum/s2/src/AlertDialog.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/@react-spectrum/s2/src/AlertDialog.tsx b/packages/@react-spectrum/s2/src/AlertDialog.tsx index ffd25220a55..7a2024fe92c 100644 --- a/packages/@react-spectrum/s2/src/AlertDialog.tsx +++ b/packages/@react-spectrum/s2/src/AlertDialog.tsx @@ -26,6 +26,7 @@ import NoticeSquare from '../s2wf-icons/S2_Icon_AlertDiamond_20_N.svg'; import {Provider} from 'react-aria-components/slots'; import {style} from '../style' with {type: 'macro'}; import {UnsafeStyles} from './style-utils' with {type: 'macro'}; +import {useId} from 'react-aria/useId'; import {useLocalizedStringFormatter} from 'react-aria/useLocalizedStringFormatter'; export interface AlertDialogProps extends DOMProps, UnsafeStyles { @@ -96,6 +97,7 @@ export const AlertDialog = forwardRef(function AlertDialog(props: AlertDialogPro children, variant = 'confirmation' } = props; + let contentId = useId(); let buttonVariant = 'primary'; if (variant === 'confirmation') { @@ -109,6 +111,7 @@ export const AlertDialog = forwardRef(function AlertDialog(props: AlertDialogPro role="alertdialog" ref={ref} size={props.size} + aria-describedby={contentId} UNSAFE_style={props.UNSAFE_style} UNSAFE_className={(props.UNSAFE_className || '')}> {({close}) => ( @@ -125,7 +128,7 @@ export const AlertDialog = forwardRef(function AlertDialog(props: AlertDialogPro - {children} + {children} {cancelLabel && )} From a3b9ee8c32618794cb0c721c7b371697121e063d Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Fri, 24 Apr 2026 10:44:28 +1000 Subject: [PATCH 08/10] refactor, add tests, fix image labels in examples --- .../@react-spectrum/s2/src/AlertDialog.tsx | 13 ++- packages/@react-spectrum/s2/src/Dialog.tsx | 25 +++-- .../s2/test/AlertDialog.test.tsx | 78 +++++++++++++ .../s2/test/StandardDialog.test.tsx | 106 ++++++++++++++++++ packages/dev/s2-docs/pages/s2/Dialog.mdx | 4 +- packages/react-aria/src/dialog/useDialog.ts | 4 +- 6 files changed, 209 insertions(+), 21 deletions(-) create mode 100644 packages/@react-spectrum/s2/test/AlertDialog.test.tsx create mode 100644 packages/@react-spectrum/s2/test/StandardDialog.test.tsx diff --git a/packages/@react-spectrum/s2/src/AlertDialog.tsx b/packages/@react-spectrum/s2/src/AlertDialog.tsx index 7a2024fe92c..e8d24881fa2 100644 --- a/packages/@react-spectrum/s2/src/AlertDialog.tsx +++ b/packages/@react-spectrum/s2/src/AlertDialog.tsx @@ -11,13 +11,14 @@ */ import AlertTriangle from '../s2wf-icons/S2_Icon_AlertTriangle_20_N.svg'; +import {AriaLabelingProps, DOMProps, DOMRef} from '@react-types/shared'; import {Button} from './Button'; import {ButtonGroup} from './ButtonGroup'; import {CenterBaseline} from './CenterBaseline'; import {chain} from 'react-aria/chain'; import {Content, Heading} from './Content'; import {Dialog} from './Dialog'; -import {DOMProps, DOMRef} from '@react-types/shared'; +import {filterDOMProps} from 'react-aria/filterDOMProps'; import {forwardRef, ReactNode} from 'react'; import {IconContext} from './Icon'; // @ts-ignore @@ -26,10 +27,9 @@ import NoticeSquare from '../s2wf-icons/S2_Icon_AlertDiamond_20_N.svg'; import {Provider} from 'react-aria-components/slots'; import {style} from '../style' with {type: 'macro'}; import {UnsafeStyles} from './style-utils' with {type: 'macro'}; -import {useId} from 'react-aria/useId'; import {useLocalizedStringFormatter} from 'react-aria/useLocalizedStringFormatter'; -export interface AlertDialogProps extends DOMProps, UnsafeStyles { +export interface AlertDialogProps extends AriaLabelingProps, DOMProps, UnsafeStyles { /** * The [visual style](https://spectrum.adobe.com/page/alert-dialog/#Options) of the AlertDialog. * @default 'confirmation' @@ -97,7 +97,6 @@ export const AlertDialog = forwardRef(function AlertDialog(props: AlertDialogPro children, variant = 'confirmation' } = props; - let contentId = useId(); let buttonVariant = 'primary'; if (variant === 'confirmation') { @@ -106,12 +105,14 @@ export const AlertDialog = forwardRef(function AlertDialog(props: AlertDialogPro buttonVariant = 'negative'; } + let domProps = filterDOMProps(props, {labelable: true}); + return ( {({close}) => ( @@ -128,7 +129,7 @@ export const AlertDialog = forwardRef(function AlertDialog(props: AlertDialogPro - {children} + {children} {cancelLabel && + + + + )} + + + ); + + let trigger = getByRole('button'); + await user.click(trigger); + act(() => {jest.runAllTimers();}); + let dialog = getByRole('dialog'); + expect(dialog).toBeVisible(); + let description = dialog.getAttribute('aria-describedby'); + expect(description).toBeNull(); + }); + + it('accepts custom aria-describedby', async () => { + let {getByRole} = render( + + Open dialog + + {({close}) => ( + <> + Dialog title +
Header
+ +

This is the content of the dialog.

+

Extra content

+
+
Don't show this again
+ + + + + + )} +
+
+ ); + + let trigger = getByRole('button'); + await user.click(trigger); + act(() => {jest.runAllTimers();}); + let dialog = getByRole('dialog'); + expect(dialog).toBeVisible(); + let description = dialog.getAttribute('aria-describedby'); + expect(description).toBeDefined(); + let content = document.getElementById(description!); + expect(content).toHaveTextContent('This is the content of the dialog.'); + }); +}); diff --git a/packages/dev/s2-docs/pages/s2/Dialog.mdx b/packages/dev/s2-docs/pages/s2/Dialog.mdx index ba071d22176..94dc70e7dfb 100644 --- a/packages/dev/s2-docs/pages/s2/Dialog.mdx +++ b/packages/dev/s2-docs/pages/s2/Dialog.mdx @@ -29,7 +29,7 @@ function Example(props) { {({close}) => ( <> - + Subscribe to our newsletter

Enter your information to subscribe to our newsletter and receive updates about new features and announcements.

@@ -76,7 +76,7 @@ function Example(props) { {({close}) => ( <> - + Dialog Title
Header
diff --git a/packages/react-aria/src/dialog/useDialog.ts b/packages/react-aria/src/dialog/useDialog.ts index 914fa3a1b69..f9df3371643 100644 --- a/packages/react-aria/src/dialog/useDialog.ts +++ b/packages/react-aria/src/dialog/useDialog.ts @@ -113,8 +113,8 @@ export function useDialog(props: AriaDialogProps, ref: RefObject Date: Fri, 24 Apr 2026 10:49:04 +1000 Subject: [PATCH 09/10] fix lint --- packages/@react-spectrum/s2/src/Dialog.tsx | 14 +++++++------- .../s2/test/StandardDialog.test.tsx | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/@react-spectrum/s2/src/Dialog.tsx b/packages/@react-spectrum/s2/src/Dialog.tsx index 78c2b4f07d9..a22151b5516 100644 --- a/packages/@react-spectrum/s2/src/Dialog.tsx +++ b/packages/@react-spectrum/s2/src/Dialog.tsx @@ -193,19 +193,19 @@ export const Dialog = forwardRef(function Dialog(props: DialogProps, ref: DOMRef } {/* Main content */} - - + - {children} - - + ]}> + {children} + + {/* Footer and button group */}
{ let user; From 47b03f1bfc52131cb383db3103470f82295f3dc0 Mon Sep 17 00:00:00 2001 From: John Costa Date: Fri, 24 Apr 2026 02:27:10 -0700 Subject: [PATCH 10/10] fix: allow aria-describedby override in v3 AlertDialog Extends SpectrumAlertDialogProps with AriaLabelingProps and passes {labelable: true} to filterDOMProps so user-supplied aria-describedby flows through to the inner Dialog instead of being stripped. Mirrors the S2 AlertDialog treatment already merged by snowystinger, and aligns v3 AlertDialog with regular v3 Dialog, which has these props transitively via AriaDialogProps. Adds a test verifying the override reaches the rendered alertdialog element. --- .../@adobe/react-spectrum/src/dialog/AlertDialog.tsx | 6 +++--- .../react-spectrum/test/dialog/AlertDialog.test.js | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/@adobe/react-spectrum/src/dialog/AlertDialog.tsx b/packages/@adobe/react-spectrum/src/dialog/AlertDialog.tsx index 35519a82f40..45ed9cafc22 100644 --- a/packages/@adobe/react-spectrum/src/dialog/AlertDialog.tsx +++ b/packages/@adobe/react-spectrum/src/dialog/AlertDialog.tsx @@ -11,6 +11,7 @@ */ import AlertMedium from '@spectrum-icons/ui/AlertMedium'; +import {AriaLabelingProps, DOMProps, DOMRef, StyleProps} from '@react-types/shared'; import {Button, SpectrumButtonProps} from '../button/Button'; import {ButtonGroup} from '../buttongroup/ButtonGroup'; import {chain} from 'react-aria/chain'; @@ -19,7 +20,6 @@ import {Content} from '../view/Content'; import {Dialog} from './Dialog'; import {DialogContext, DialogContextValue} from './context'; import {Divider} from '../divider/Divider'; -import {DOMProps, DOMRef, StyleProps} from '@react-types/shared'; import {filterDOMProps} from 'react-aria/filterDOMProps'; import {Heading} from '../text/Heading'; import intlMessages from '../../intl/dialog/*.json'; @@ -29,7 +29,7 @@ import styles from '@adobe/spectrum-css-temp/components/dialog/vars.css'; import {useLocalizedStringFormatter} from 'react-aria/useLocalizedStringFormatter'; import {useStyleProps} from '../utils/styleProps'; -export interface SpectrumAlertDialogProps extends DOMProps, StyleProps { +export interface SpectrumAlertDialogProps extends AriaLabelingProps, DOMProps, StyleProps { /** The [visual style](https://spectrum.adobe.com/page/alert-dialog/#Options) of the AlertDialog. */ variant?: 'confirmation' | 'information' | 'destructive' | 'error' | 'warning', /** The title of the AlertDialog. */ @@ -100,7 +100,7 @@ export const AlertDialog = forwardRef(function AlertDialog(props: SpectrumAlertD size="M" role="alertdialog" ref={ref} - {...filterDOMProps(props)}> + {...filterDOMProps(props, {labelable: true})}> {title} {(variant === 'error' || variant === 'warning') && + + Content body + + + ); + + expect(getByRole('alertdialog')).toHaveAttribute('aria-describedby', 'content-id'); + }); });