-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #64 from aversini/feat-introducing-LiveRegion-comp…
…onent feat: introducing LiveRegion component
- Loading branch information
Showing
6 changed files
with
689 additions
and
2 deletions.
There are no files selected for viewing
This file contains 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
70 changes: 70 additions & 0 deletions
70
packages/ui-components/src/components/private/LiveRegion/LiveRegion.tsx
This file contains 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,70 @@ | ||
import clsx from "clsx"; | ||
import { useEffect, useRef } from "react"; | ||
|
||
import { VISUALLY_HIDDEN_CLASSNAME } from "../../../common/constants"; | ||
import { DEFAULT_POLITENESS_BY_ROLE } from "./constants"; | ||
import type { LiveRegionProps } from "./LiveRegionTypes"; | ||
import { conditionallyDelayAnnouncement } from "./utilities"; | ||
|
||
/** | ||
* The `LiveRegion` component abstracts the logic for | ||
* rendering live region content that consistently announces | ||
* across assistive technologies (e.g. JAWS, VoiceOver, | ||
* NVDA). | ||
*/ | ||
export function LiveRegion({ | ||
children, | ||
className, | ||
politeness: politenessProp, | ||
role = null, | ||
announcementDelay, | ||
clearAnnouncementDelay, | ||
onAnnouncementClear, | ||
visible, | ||
|
||
...otherProps | ||
}: LiveRegionProps) { | ||
const liveRegionRef = useRef<HTMLDivElement>(null); | ||
const announcementTimeoutRef = useRef(); | ||
const clearAnnouncementTimeoutRef = useRef(); | ||
|
||
let politeness = politenessProp; | ||
/** | ||
* We check `undefined` since it is our default, | ||
* and we want to honor when the user supplies `null`. | ||
*/ | ||
if (typeof politeness === "undefined") { | ||
politeness = role ? DEFAULT_POLITENESS_BY_ROLE[role] : "assertive"; | ||
} | ||
|
||
useEffect(() => { | ||
conditionallyDelayAnnouncement({ | ||
announcementTimeoutRef, | ||
announcementDelay, | ||
children, | ||
liveRegionRef, | ||
clearAnnouncementDelay, | ||
clearAnnouncementTimeoutRef, | ||
onAnnouncementClear, | ||
}); | ||
}, [ | ||
children, | ||
announcementDelay, | ||
clearAnnouncementDelay, | ||
onAnnouncementClear, | ||
]); | ||
|
||
const generatedClassName = clsx(className, { | ||
[VISUALLY_HIDDEN_CLASSNAME]: !visible, | ||
}); | ||
|
||
return ( | ||
<div | ||
ref={liveRegionRef} | ||
aria-live={politeness as "polite" | "assertive" | "off" | undefined} | ||
{...(role && { role: role })} | ||
className={generatedClassName} | ||
{...otherProps} | ||
/> | ||
); | ||
} |
69 changes: 69 additions & 0 deletions
69
packages/ui-components/src/components/private/LiveRegion/LiveRegionTypes.d.ts
This file contains 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,69 @@ | ||
import { ROLES } from "./constants"; | ||
|
||
export type PolitenessByRole = { | ||
[key: string]: any; | ||
}; | ||
|
||
export type ClearAnnouncementProps = { | ||
liveRegionRef: React.RefObject<HTMLElement | undefined>; | ||
onAnnouncementClear?: () => void; | ||
}; | ||
|
||
export type announceProps = { | ||
children: React.ReactNode; | ||
liveRegionRef: React.RefObject<HTMLElement | undefined>; | ||
clearAnnouncementDelay?: number; | ||
clearAnnouncementTimeoutRef: React.MutableRefObject< | ||
NodeJS.Timeout | number | null | undefined | ||
>; | ||
onAnnouncementClear?: () => void; | ||
}; | ||
|
||
export type conditionallyDelayAnnouncementProps = { | ||
children: React.ReactNode; | ||
liveRegionRef: React.RefObject<HTMLElement | undefined>; | ||
announcementTimeoutRef: React.MutableRefObject< | ||
NodeJS.Timeout | null | undefined | ||
>; | ||
announcementDelay?: number; | ||
clearAnnouncementDelay?: number; | ||
clearAnnouncementTimeoutRef: React.MutableRefObject< | ||
NodeJS.Timeout | number | null | undefined | ||
>; | ||
onAnnouncementClear?: () => void; | ||
}; | ||
|
||
export type LiveRegionProps = { | ||
/** | ||
* The content to be announced by the live region. | ||
*/ | ||
children: React.ReactNode; | ||
/** | ||
* The `className` to apply to the live region. | ||
*/ | ||
className?: string; | ||
/** | ||
* The `aria-live` politeness level to apply to the live region. | ||
*/ | ||
politeness?: "polite" | "assertive" | "off" | null | undefined; | ||
/** | ||
* The `role` to apply to the live region. | ||
*/ | ||
role?: typeof ROLES.ALERT | "status" | null | undefined; | ||
/** | ||
* Whether or not the live region should be visible. | ||
*/ | ||
visible?: boolean; | ||
/** | ||
* The number of milliseconds to wait before announcing the content. | ||
*/ | ||
announcementDelay?: number; | ||
/** | ||
* The number of milliseconds to wait before clearing the announcement. | ||
*/ | ||
clearAnnouncementDelay?: number; | ||
/** | ||
* A callback to be invoked when the announcement is cleared. | ||
*/ | ||
onAnnouncementClear?: () => void; | ||
}; |
Oops, something went wrong.