|
1 | 1 | import {announceFromElement} from '@primer/live-region-element' |
2 | 2 | import type React from 'react' |
3 | 3 | import {useEffect, useRef, useState, type ElementRef} from 'react' |
4 | | -import Box from '../Box' |
5 | 4 | import {useEffectOnce} from '../internal/hooks/useEffectOnce' |
6 | 5 | import {useEffectCallback} from '../internal/hooks/useEffectCallback' |
7 | | - |
8 | | -export type AnnounceProps = React.ComponentPropsWithoutRef<typeof Box> & { |
9 | | - /** |
10 | | - * Specify if the content of the element should be announced when this |
11 | | - * component is rendered and is not hidden |
12 | | - * @default false |
13 | | - */ |
14 | | - announceOnShow?: boolean |
15 | | - |
16 | | - /** |
17 | | - * Specify if the element is hidden |
18 | | - * @default false |
19 | | - */ |
20 | | - hidden?: boolean |
21 | | - |
22 | | - /** |
23 | | - * Provide a delay in milliseconds before the announcement is made. This will |
24 | | - * only work with `polite` announcements |
25 | | - */ |
26 | | - delayMs?: number |
27 | | - |
28 | | - /** |
29 | | - * The politeness level to use for the announcement |
30 | | - * @default 'polite' |
31 | | - */ |
32 | | - politeness?: 'assertive' | 'polite' |
33 | | -} |
| 6 | +import type {PolymorphicProps} from '../utils/polymorphic2' |
| 7 | + |
| 8 | +export type AnnounceProps<As extends React.ElementType> = PolymorphicProps< |
| 9 | + 'div', |
| 10 | + As, |
| 11 | + { |
| 12 | + /** |
| 13 | + * Specify if the content of the element should be announced when this |
| 14 | + * component is rendered and is not hidden |
| 15 | + * @default false |
| 16 | + */ |
| 17 | + announceOnShow?: boolean |
| 18 | + |
| 19 | + /** |
| 20 | + * Specify if the element is hidden |
| 21 | + * @default false |
| 22 | + */ |
| 23 | + hidden?: boolean |
| 24 | + |
| 25 | + /** |
| 26 | + * Provide a delay in milliseconds before the announcement is made. This will |
| 27 | + * only work with `polite` announcements |
| 28 | + */ |
| 29 | + delayMs?: number |
| 30 | + |
| 31 | + /** |
| 32 | + * The politeness level to use for the announcement |
| 33 | + * @default 'polite' |
| 34 | + */ |
| 35 | + politeness?: 'assertive' | 'polite' |
| 36 | + } |
| 37 | +> |
34 | 38 |
|
35 | 39 | /** |
36 | 40 | * `Announce` is a component that will announce the text content of the |
37 | 41 | * `children` passed in to screen readers using the given politeness level. It |
38 | 42 | * will also announce any changes to the text content of `children` |
39 | 43 | */ |
40 | | -export function Announce({ |
41 | | - announceOnShow = true, |
42 | | - children, |
43 | | - delayMs, |
44 | | - hidden = false, |
45 | | - politeness = 'polite', |
46 | | - ...rest |
47 | | -}: AnnounceProps) { |
| 44 | +export function Announce<As extends React.ElementType = 'div'>(props: AnnounceProps<As>) { |
| 45 | + const { |
| 46 | + as: BaseComponent = 'div', |
| 47 | + announceOnShow = true, |
| 48 | + children, |
| 49 | + delayMs, |
| 50 | + hidden = false, |
| 51 | + politeness = 'polite', |
| 52 | + ...rest |
| 53 | + } = props |
48 | 54 | const ref = useRef<ElementRef<'div'>>(null) |
49 | 55 | const [previousAnnouncementText, setPreviousAnnouncementText] = useState<string | null>(null) |
50 | 56 | const savedAnnouncement = useRef<ReturnType<typeof announceFromElement> | null>(null) |
@@ -127,9 +133,9 @@ export function Announce({ |
127 | 133 | }, []) |
128 | 134 |
|
129 | 135 | return ( |
130 | | - <Box {...rest} ref={ref}> |
| 136 | + <BaseComponent {...rest} ref={ref}> |
131 | 137 | {children} |
132 | | - </Box> |
| 138 | + </BaseComponent> |
133 | 139 | ) |
134 | 140 | } |
135 | 141 |
|
|
0 commit comments