diff --git a/superset-frontend/packages/superset-core/src/components/Alert/Alert.test.tsx b/superset-frontend/packages/superset-core/src/components/Alert/Alert.test.tsx index cb0e8092f7d7..24f11f10a815 100644 --- a/superset-frontend/packages/superset-core/src/components/Alert/Alert.test.tsx +++ b/superset-frontend/packages/superset-core/src/components/Alert/Alert.test.tsx @@ -17,10 +17,44 @@ * under the License. */ -import { render } from '../../testing'; +import { render, screen } from '../../testing'; import { Alert } from '.'; test('renders Alert with default props', async () => { const { container } = render(); expect(container).toHaveTextContent('Default message'); }); + +// WCAG 3.3.1 - Error Identification accessibility tests +test('renders with role="alert" for screen reader announcement', () => { + render(Error message); + expect(screen.getByRole('alert')).toBeInTheDocument(); +}); + +test('renders with aria-atomic="true" so full content is announced', () => { + render(Error message); + expect(screen.getByRole('alert')).toHaveAttribute('aria-atomic', 'true'); +}); + +test('uses aria-live="assertive" for error alerts', () => { + render(Error message); + expect(screen.getByRole('alert')).toHaveAttribute( + 'aria-live', + 'assertive', + ); +}); + +test('uses aria-live="polite" for warning alerts', () => { + render(Warning message); + expect(screen.getByRole('alert')).toHaveAttribute('aria-live', 'polite'); +}); + +test('uses aria-live="polite" for info alerts', () => { + render(Info message); + expect(screen.getByRole('alert')).toHaveAttribute('aria-live', 'polite'); +}); + +test('uses aria-live="polite" for success alerts', () => { + render(Success message); + expect(screen.getByRole('alert')).toHaveAttribute('aria-live', 'polite'); +}); diff --git a/superset-frontend/packages/superset-core/src/components/Alert/index.tsx b/superset-frontend/packages/superset-core/src/components/Alert/index.tsx index ff593b853a57..bda693a2721e 100644 --- a/superset-frontend/packages/superset-core/src/components/Alert/index.tsx +++ b/superset-frontend/packages/superset-core/src/components/Alert/index.tsx @@ -71,6 +71,7 @@ export const Alert = (props: AlertProps) => { return ( { const hasError = !!errorMessage; + const errorDescriptionId = hasError ? `${id || props.name}-error-description` : undefined; return ( @@ -78,13 +79,23 @@ export const LabeledErrorBoundInput = ({ validateStatus={ isValidating ? 'validating' : hasError ? 'error' : 'success' } - help={errorMessage || helpText} + help={ + hasError ? ( + + {errorMessage} + + ) : ( + helpText + ) + } hasFeedback={!!hasError} > {visibilityToggle || props.name === 'password' ? ( visible ? ( @@ -99,7 +110,12 @@ export const LabeledErrorBoundInput = ({ role="textbox" /> ) : ( - + )} {get_url && description ? (