-
Notifications
You must be signed in to change notification settings - Fork 6
Add ARIA live region for screen reader announcements of search results #448
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
29cd0f6
Initial plan
Copilot 59f2ed5
Add ARIA live region to announce search results count for screen readers
Copilot 2497db5
Refactor: Extract results announcement logic into helper function for…
Copilot 301cb99
feat: useAnnounce for accessibility introduced
tnaum-ms aa5ae1c
Merge branch 'next' into copilot/fix-screenreader-results-announcement
tnaum-ms 55340bb
feat: useAnnounce for accessibility introduced: improved
tnaum-ms c225453
Fix: Include document count in screen reader announcements
Copilot da4bed1
Revert "Fix: Include document count in screen reader announcements"
tnaum-ms File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| export { useAnnounce } from './useAnnounce'; | ||
| export type { UseAnnounceOptions, UseAnnounceReturn } from './useAnnounce'; |
111 changes: 111 additions & 0 deletions
111
src/webviews/api/webview-client/accessibility/useAnnounce.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| import * as React from 'react'; | ||
| import { useCallback, useRef, useState } from 'react'; | ||
|
|
||
| /** | ||
| * Options for the useAnnounce hook | ||
| */ | ||
| export interface UseAnnounceOptions { | ||
| /** | ||
| * The politeness level of the announcement. | ||
| * - 'polite': Waits for the user to finish their current activity before announcing (default) | ||
| * - 'assertive': Interrupts the user immediately (use sparingly) | ||
| * @default 'polite' | ||
| */ | ||
| politeness?: 'polite' | 'assertive'; | ||
| } | ||
|
|
||
| /** | ||
| * Return type for the useAnnounce hook | ||
| */ | ||
| export interface UseAnnounceReturn { | ||
| /** | ||
| * Function to trigger a screen reader announcement | ||
| * @param message - The message to announce. Pass empty string to clear. | ||
| */ | ||
| announce: (message: string) => void; | ||
|
|
||
| /** | ||
| * React element to render in your component tree. | ||
| * This creates the ARIA live region that screen readers listen to. | ||
| * Place this anywhere in your JSX - it's visually hidden but accessible. | ||
| */ | ||
| AnnouncerElement: React.ReactElement; | ||
| } | ||
|
|
||
| /** | ||
| * A React hook for making screen reader announcements using ARIA live regions. | ||
| * | ||
| * This hook provides a clean API for announcing dynamic content changes to screen readers, | ||
| * following WCAG 4.1.3 (Status Messages) guidelines. It abstracts the implementation details | ||
| * of ARIA live regions, making it easy to announce search results, loading states, errors, etc. | ||
| * | ||
| * @example | ||
| * ```tsx | ||
| * const { announce, AnnouncerElement } = useAnnounce(); | ||
| * | ||
| * useEffect(() => { | ||
| * if (!isLoading && hasResults !== undefined) { | ||
| * announce(hasResults ? 'Results found' : 'No results found'); | ||
| * } | ||
| * }, [isLoading, hasResults, announce]); | ||
| * | ||
| * return ( | ||
| * <div> | ||
| * {AnnouncerElement} | ||
| * {// ... rest of your UI} | ||
| * </div> | ||
| * ); | ||
| * ``` | ||
| * | ||
| * @param options - Configuration options for the announcer | ||
| * @returns Object containing the announce function and AnnouncerElement to render | ||
| * | ||
| * @see https://www.w3.org/WAI/WCAG21/Understanding/status-messages.html | ||
| */ | ||
| export function useAnnounce(options: UseAnnounceOptions = {}): UseAnnounceReturn { | ||
| const { politeness = 'polite' } = options; | ||
|
|
||
| const [message, setMessage] = useState<string>(''); | ||
| const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null); | ||
|
|
||
| const announce = useCallback((newMessage: string) => { | ||
| // Clear any pending timeout | ||
| if (timeoutRef.current) { | ||
| clearTimeout(timeoutRef.current); | ||
| } | ||
|
|
||
| // Clear the message first to ensure re-announcement of identical messages | ||
| setMessage(''); | ||
|
|
||
| // Set the new message after a brief delay to ensure the live region updates | ||
| timeoutRef.current = setTimeout(() => { | ||
| setMessage(newMessage); | ||
| }, 100); | ||
| }, []); | ||
|
|
||
| // Styles that visually hide the element but keep it accessible to screen readers | ||
| const srOnlyStyles: React.CSSProperties = { | ||
| position: 'absolute', | ||
| width: '1px', | ||
| height: '1px', | ||
| padding: 0, | ||
| margin: '-1px', | ||
| overflow: 'hidden', | ||
| clip: 'rect(0, 0, 0, 0)', | ||
| whiteSpace: 'nowrap', | ||
| borderWidth: 0, | ||
| }; | ||
|
|
||
| const AnnouncerElement = ( | ||
| <div role="status" aria-live={politeness} aria-atomic="true" style={srOnlyStyles}> | ||
| {message} | ||
| </div> | ||
| ); | ||
|
|
||
| return { announce, AnnouncerElement }; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.