-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
fix: Fix Talkback focus cursor movement when inside Dialogs #7478
base: main
Are you sure you want to change the base?
Conversation
…opes (e.g. Dialogs)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can now tab through the iframe on chrome
if (raf.current) { | ||
cancelAnimationFrame(raf.current); | ||
} | ||
raf.current = requestAnimationFrame(() => { | ||
// Use document.activeElement instead of e.relatedTarget so we can tell if user clicked into iframe | ||
if (ownerDocument.activeElement && shouldContainFocus(scopeRef) && !isElementInChildScope(ownerDocument.activeElement, scopeRef)) { | ||
if (e.relatedTarget && shouldContainFocus(scopeRef) && !isElementInChildScope(e.relatedTarget, scopeRef)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just realizes this breaks this story, digging
Closing for now, trying to figure out an alternative fix. Filed https://issuetracker.google.com/issues/384844019 to see what has changed recently, but also noticed several other libraries don't have this issue with most notably Material UI exhibiting something similar via https://mui.com/material-ui/react-dialog/ but not with https://base-ui.com/react/components/dialog so maybe there is something we can gather from there |
// Patches infinite focus coersion loop for Android Talkback where the user isn't able to move the virtual cursor | ||
// if within a containing focus scope. Bug filed against Chrome: https://issuetracker.google.com/issues/384844019. | ||
// Note that this means focus can leave focus containing modals due to this, but it is isolated to Chrome Talkback. | ||
let modality = getInteractionModality(); | ||
let shouldSkipFocusRestore = (modality === 'virtual' || modality === null) && isAndroid() && isChrome(); | ||
|
||
// Use document.activeElement instead of e.relatedTarget so we can tell if user clicked into iframe | ||
if (ownerDocument.activeElement && shouldContainFocus(scopeRef) && !isElementInChildScope(ownerDocument.activeElement, scopeRef)) { | ||
if (!shouldSkipFocusRestore && ownerDocument.activeElement && shouldContainFocus(scopeRef) && !isElementInChildScope(ownerDocument.activeElement, scopeRef)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As stated here, this is a temporary patch. Note that this will break use cases where focus is truly lost to the body such as this story on main where due to the dialog's content changing, the previously focused button disappears. We would usually recoerce focus back into the dialog but at the moment we cannot differentiate between that case and if the user is simply navigating between elements in the FocusScope in Chrome Talkback
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also note that this is indeed a Chrome specific bug, Firefox retains DOM focus on whatever Talkback was previously virtually focusing until you trigger a click via double tapping on a different element, can be tested via: https://64qz97.csb.app/ . This is most likely what Chrome's behavior used to be which is why this code used to work
## API Changes
@react-aria/utils/@react-aria/utils:useEnterAnimation-useEnterAnimation {
- ref: RefObject<HTMLElement | null>
- isReady: boolean
- returnVal: undefined
-} /@react-aria/utils:useExitAnimation-useExitAnimation {
- ref: RefObject<HTMLElement | null>
- isOpen: boolean
- returnVal: undefined
-} @react-spectrum/s2/@react-spectrum/s2:ActionBar-ActionBar {
- children: ReactNode
- isEmphasized?: boolean
- onClearSelection?: () => void
- scrollRef?: RefObject<HTMLElement | null>
- selectedItemCount?: number | 'all'
- slot?: string | null
-} /@react-spectrum/s2:ActionBarContext-ActionBarContext {
- UNTYPED
-} /@react-spectrum/s2:ActionButton ActionButton {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
children?: ReactNode
excludeFromTabOrder?: boolean
form?: string
formAction?: string
formEncType?: string
formMethod?: string
formNoValidate?: boolean
formTarget?: string
id?: string
isDisabled?: boolean
isQuiet?: boolean
name?: string
onBlur?: (FocusEvent<Target>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
- staticColor?: 'black' | 'white' | 'auto'
+ staticColor?: 'black' | 'white'
styles?: StylesProp
type?: 'button' | 'submit' | 'reset' = 'button'
value?: string
} /@react-spectrum/s2:ActionButtonGroup ActionButtonGroup {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
- aria-describedby?: string
- aria-details?: string
- aria-label?: string
- aria-labelledby?: string
children: ReactNode
density?: 'compact' | 'regular' = "regular"
isDisabled?: boolean
isJustified?: boolean
isQuiet?: boolean
orientation?: 'horizontal' | 'vertical' = 'horizontal'
size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = "M"
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesPropWithHeight
} /@react-spectrum/s2:Button Button {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
children?: ReactNode
excludeFromTabOrder?: boolean
fillStyle?: 'fill' | 'outline' = 'fill'
form?: string
formAction?: string
formEncType?: string
formMethod?: string
formNoValidate?: boolean
formTarget?: string
id?: string
isDisabled?: boolean
isPending?: boolean
name?: string
onBlur?: (FocusEvent<Target>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
type?: 'button' | 'submit' | 'reset' = 'button'
value?: string
- variant?: 'primary' | 'secondary' | 'accent' | 'negative' | 'premium' | 'genai' = 'primary'
+ variant?: 'primary' | 'secondary' | 'accent' | 'negative' = 'primary'
} /@react-spectrum/s2:LinkButton LinkButton {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean
children?: ReactNode
download?: boolean | string
fillStyle?: 'fill' | 'outline' = 'fill'
href?: Href
hrefLang?: string
isDisabled?: boolean
onBlur?: (FocusEvent<Target>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onHoverChange?: (boolean) => void
onHoverEnd?: (HoverEvent) => void
onHoverStart?: (HoverEvent) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
ping?: string
referrerPolicy?: HTMLAttributeReferrerPolicy
rel?: string
routerOptions?: RouterOptions
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
target?: HTMLAttributeAnchorTarget
- variant?: 'primary' | 'secondary' | 'accent' | 'negative' | 'premium' | 'genai' = 'primary'
+ variant?: 'primary' | 'secondary' | 'accent' | 'negative' = 'primary'
} /@react-spectrum/s2:CardView CardView <T extends {}> {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
children?: ReactNode | (T) => ReactNode
defaultSelectedKeys?: 'all' | Iterable<Key>
density?: 'compact' | 'regular' | 'spacious' = 'regular'
dependencies?: Array<any>
disabledBehavior?: DisabledBehavior
disabledKeys?: Iterable<Key>
disallowEmptySelection?: boolean
dragAndDropHooks?: DragAndDropHooks
id?: string
items?: Iterable<T>
layout?: 'grid' | 'waterfall' = 'grid'
loadingState?: LoadingState
onAction?: (Key) => void
onLoadMore?: () => void
onScroll?: (UIEvent<Element>) => void
onSelectionChange?: (Selection) => void
- renderActionBar?: ('all' | Set<Key>) => ReactElement
renderEmptyState?: (GridListRenderProps) => ReactNode
selectedKeys?: 'all' | Iterable<Key>
selectionMode?: SelectionMode
selectionStyle?: 'checkbox' | 'highlight' = 'checkbox'
slot?: string | null
styles?: StylesPropWithHeight
variant?: 'primary' | 'secondary' | 'tertiary' | 'quiet' = 'primary'
} /@react-spectrum/s2:CloseButton CloseButton {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
isDisabled?: boolean
- onPress?: (PressEvent) => void
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
} /@react-spectrum/s2:Divider Divider {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
id?: string
orientation?: 'horizontal' | 'vertical' = 'horizontal'
size?: 'S' | 'M' | 'L' = 'M'
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
} /@react-spectrum/s2:Link Link {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean
children?: ReactNode
download?: boolean | string
href?: Href
hrefLang?: string
isQuiet?: boolean
isStandalone?: boolean
onBlur?: (FocusEvent<Target>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
ping?: string
referrerPolicy?: HTMLAttributeReferrerPolicy
rel?: string
routerOptions?: RouterOptions
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
target?: HTMLAttributeAnchorTarget
variant?: 'primary' | 'secondary' = 'primary'
} /@react-spectrum/s2:Meter Meter {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
formatOptions?: Intl.NumberFormatOptions = {style: 'percent'}
id?: string
label?: ReactNode
labelPosition?: LabelPosition = 'top'
maxValue?: number = 100
minValue?: number = 0
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
value?: number = 0
valueLabel?: ReactNode
variant?: 'informative' | 'positive' | 'notice' | 'negative' = 'informative' /@react-spectrum/s2:ProgressBar ProgressBar {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
formatOptions?: Intl.NumberFormatOptions = {style: 'percent'}
id?: string
isIndeterminate?: boolean
label?: ReactNode
labelPosition?: LabelPosition = 'top'
maxValue?: number = 100
minValue?: number = 0
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
value?: number = 0
valueLabel?: ReactNode
} /@react-spectrum/s2:ProgressCircle ProgressCircle {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
id?: string
isIndeterminate?: boolean
maxValue?: number = 100
minValue?: number = 0
size?: 'S' | 'M' | 'L' = 'M'
slot?: string | null
- staticColor?: 'black' | 'white' | 'auto'
+ staticColor?: 'black' | 'white'
styles?: StylesPropWithHeight
value?: number = 0
} /@react-spectrum/s2:TableView TableView {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
children?: ReactNode
defaultSelectedKeys?: 'all' | Iterable<Key>
density?: 'compact' | 'spacious' | 'regular' = 'regular'
disabledKeys?: Iterable<Key>
disallowEmptySelection?: boolean
isQuiet?: boolean
loadingState?: LoadingState
onAction?: (Key) => void
onLoadMore?: () => any
onResize?: (Map<Key, ColumnSize>) => void
onResizeEnd?: (Map<Key, ColumnSize>) => void
onResizeStart?: (Map<Key, ColumnSize>) => void
onSelectionChange?: (Selection) => void
onSortChange?: (SortDescriptor) => any
overflowMode?: 'wrap' | 'truncate' = 'truncate'
- renderActionBar?: ('all' | Set<Key>) => ReactElement
selectedKeys?: 'all' | Iterable<Key>
selectionMode?: SelectionMode
slot?: string | null
sortDescriptor?: SortDescriptor
} /@react-spectrum/s2:ToggleButton ToggleButton {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
children?: ReactNode
defaultSelected?: boolean
excludeFromTabOrder?: boolean
id?: Key
isDisabled?: boolean
isEmphasized?: boolean
isQuiet?: boolean
isSelected?: boolean
onBlur?: (FocusEvent<Target>) => void
onChange?: (boolean) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
- staticColor?: 'black' | 'white' | 'auto'
+ staticColor?: 'black' | 'white'
styles?: StylesProp
type?: 'button' | 'submit' | 'reset' = 'button'
} /@react-spectrum/s2:ToggleButtonGroup ToggleButtonGroup {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
children: ReactNode
defaultSelectedKeys?: Iterable<Key>
density?: 'compact' | 'regular' = "regular"
disallowEmptySelection?: boolean
isDisabled?: boolean
isEmphasized?: boolean
isJustified?: boolean
isQuiet?: boolean
onSelectionChange?: (Set<Key>) => void
orientation?: 'horizontal' | 'vertical' = 'horizontal'
selectedKeys?: Iterable<Key>
selectionMode?: 'single' | 'multiple'
size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = "M"
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesPropWithHeight
} /@react-spectrum/s2:ActionBarProps-ActionBarProps {
- children: ReactNode
- isEmphasized?: boolean
- onClearSelection?: () => void
- scrollRef?: RefObject<HTMLElement | null>
- selectedItemCount?: number | 'all'
- slot?: string | null
-} /@react-spectrum/s2:ActionButtonProps ActionButtonProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
children?: ReactNode
excludeFromTabOrder?: boolean
form?: string
formAction?: string
formEncType?: string
formMethod?: string
formNoValidate?: boolean
formTarget?: string
id?: string
isDisabled?: boolean
isQuiet?: boolean
name?: string
onBlur?: (FocusEvent<Target>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
- staticColor?: 'black' | 'white' | 'auto'
+ staticColor?: 'black' | 'white'
styles?: StylesProp
type?: 'button' | 'submit' | 'reset' = 'button'
value?: string
} /@react-spectrum/s2:ActionButtonGroupProps ActionButtonGroupProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
- aria-describedby?: string
- aria-details?: string
- aria-label?: string
- aria-labelledby?: string
children: ReactNode
density?: 'compact' | 'regular' = "regular"
isDisabled?: boolean
isJustified?: boolean
isQuiet?: boolean
orientation?: 'horizontal' | 'vertical' = 'horizontal'
size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = "M"
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesPropWithHeight
} /@react-spectrum/s2:ButtonProps ButtonProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
children?: ReactNode
excludeFromTabOrder?: boolean
fillStyle?: 'fill' | 'outline' = 'fill'
form?: string
formAction?: string
formEncType?: string
formMethod?: string
formNoValidate?: boolean
formTarget?: string
id?: string
isDisabled?: boolean
isPending?: boolean
name?: string
onBlur?: (FocusEvent<Target>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
type?: 'button' | 'submit' | 'reset' = 'button'
value?: string
- variant?: 'primary' | 'secondary' | 'accent' | 'negative' | 'premium' | 'genai' = 'primary'
+ variant?: 'primary' | 'secondary' | 'accent' | 'negative' = 'primary'
} /@react-spectrum/s2:LinkButtonProps LinkButtonProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean
children?: ReactNode
download?: boolean | string
fillStyle?: 'fill' | 'outline' = 'fill'
href?: Href
hrefLang?: string
isDisabled?: boolean
onBlur?: (FocusEvent<Target>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onHoverChange?: (boolean) => void
onHoverEnd?: (HoverEvent) => void
onHoverStart?: (HoverEvent) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
ping?: string
referrerPolicy?: HTMLAttributeReferrerPolicy
rel?: string
routerOptions?: RouterOptions
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
target?: HTMLAttributeAnchorTarget
- variant?: 'primary' | 'secondary' | 'accent' | 'negative' | 'premium' | 'genai' = 'primary'
+ variant?: 'primary' | 'secondary' | 'accent' | 'negative' = 'primary'
} /@react-spectrum/s2:CardViewProps CardViewProps <T> {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
children?: ReactNode | (T) => ReactNode
defaultSelectedKeys?: 'all' | Iterable<Key>
density?: 'compact' | 'regular' | 'spacious' = 'regular'
dependencies?: Array<any>
disabledBehavior?: DisabledBehavior
disabledKeys?: Iterable<Key>
disallowEmptySelection?: boolean
dragAndDropHooks?: DragAndDropHooks
id?: string
items?: Iterable<T>
layout?: 'grid' | 'waterfall' = 'grid'
loadingState?: LoadingState
onAction?: (Key) => void
onLoadMore?: () => void
onScroll?: (UIEvent<Element>) => void
onSelectionChange?: (Selection) => void
- renderActionBar?: ('all' | Set<Key>) => ReactElement
renderEmptyState?: (GridListRenderProps) => ReactNode
selectedKeys?: 'all' | Iterable<Key>
selectionMode?: SelectionMode
selectionStyle?: 'checkbox' | 'highlight' = 'checkbox'
slot?: string | null
styles?: StylesPropWithHeight
variant?: 'primary' | 'secondary' | 'tertiary' | 'quiet' = 'primary'
} /@react-spectrum/s2:CloseButtonProps CloseButtonProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
isDisabled?: boolean
- onPress?: (PressEvent) => void
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
} /@react-spectrum/s2:DividerProps DividerProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
id?: string
orientation?: 'horizontal' | 'vertical' = 'horizontal'
size?: 'S' | 'M' | 'L' = 'M'
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
} /@react-spectrum/s2:LinkProps LinkProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean
children?: ReactNode
download?: boolean | string
href?: Href
hrefLang?: string
isQuiet?: boolean
isStandalone?: boolean
onBlur?: (FocusEvent<Target>) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
ping?: string
referrerPolicy?: HTMLAttributeReferrerPolicy
rel?: string
routerOptions?: RouterOptions
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
target?: HTMLAttributeAnchorTarget
variant?: 'primary' | 'secondary' = 'primary'
} /@react-spectrum/s2:MeterProps MeterProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
formatOptions?: Intl.NumberFormatOptions = {style: 'percent'}
id?: string
label?: ReactNode
labelPosition?: LabelPosition = 'top'
maxValue?: number = 100
minValue?: number = 0
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
value?: number = 0
valueLabel?: ReactNode
variant?: 'informative' | 'positive' | 'notice' | 'negative' = 'informative' /@react-spectrum/s2:ProgressBarProps ProgressBarProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
formatOptions?: Intl.NumberFormatOptions = {style: 'percent'}
id?: string
isIndeterminate?: boolean
label?: ReactNode
labelPosition?: LabelPosition = 'top'
maxValue?: number = 100
minValue?: number = 0
size?: 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesProp
value?: number = 0
valueLabel?: ReactNode
} /@react-spectrum/s2:ProgressCircleProps ProgressCircleProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
id?: string
isIndeterminate?: boolean
maxValue?: number = 100
minValue?: number = 0
size?: 'S' | 'M' | 'L' = 'M'
slot?: string | null
- staticColor?: 'black' | 'white' | 'auto'
+ staticColor?: 'black' | 'white'
styles?: StylesPropWithHeight
value?: number = 0
} /@react-spectrum/s2:TableViewProps TableViewProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
children?: ReactNode
defaultSelectedKeys?: 'all' | Iterable<Key>
density?: 'compact' | 'spacious' | 'regular' = 'regular'
disabledKeys?: Iterable<Key>
disallowEmptySelection?: boolean
isQuiet?: boolean
loadingState?: LoadingState
onAction?: (Key) => void
onLoadMore?: () => any
onResize?: (Map<Key, ColumnSize>) => void
onResizeEnd?: (Map<Key, ColumnSize>) => void
onResizeStart?: (Map<Key, ColumnSize>) => void
onSelectionChange?: (Selection) => void
onSortChange?: (SortDescriptor) => any
overflowMode?: 'wrap' | 'truncate' = 'truncate'
- renderActionBar?: ('all' | Set<Key>) => ReactElement
selectedKeys?: 'all' | Iterable<Key>
selectionMode?: SelectionMode
slot?: string | null
sortDescriptor?: SortDescriptor
} /@react-spectrum/s2:ToggleButtonProps ToggleButtonProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-controls?: string
aria-describedby?: string
aria-details?: string
aria-expanded?: boolean | 'true' | 'false'
aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
aria-label?: string
aria-labelledby?: string
aria-pressed?: boolean | 'true' | 'false' | 'mixed'
autoFocus?: boolean
children?: ReactNode
defaultSelected?: boolean
excludeFromTabOrder?: boolean
id?: Key
isDisabled?: boolean
isEmphasized?: boolean
isQuiet?: boolean
isSelected?: boolean
onBlur?: (FocusEvent<Target>) => void
onChange?: (boolean) => void
onFocus?: (FocusEvent<Target>) => void
onFocusChange?: (boolean) => void
onKeyDown?: (KeyboardEvent) => void
onKeyUp?: (KeyboardEvent) => void
onPress?: (PressEvent) => void
onPressChange?: (boolean) => void
onPressEnd?: (PressEvent) => void
onPressStart?: (PressEvent) => void
onPressUp?: (PressEvent) => void
preventFocusOnPress?: boolean
size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = 'M'
slot?: string | null
- staticColor?: 'black' | 'white' | 'auto'
+ staticColor?: 'black' | 'white'
styles?: StylesProp
type?: 'button' | 'submit' | 'reset' = 'button'
} /@react-spectrum/s2:ToggleButtonGroupProps ToggleButtonGroupProps {
UNSAFE_className?: string
UNSAFE_style?: CSSProperties
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
children: ReactNode
defaultSelectedKeys?: Iterable<Key>
density?: 'compact' | 'regular' = "regular"
disallowEmptySelection?: boolean
isDisabled?: boolean
isEmphasized?: boolean
isJustified?: boolean
isQuiet?: boolean
onSelectionChange?: (Set<Key>) => void
orientation?: 'horizontal' | 'vertical' = 'horizontal'
selectedKeys?: Iterable<Key>
selectionMode?: 'single' | 'multiple'
size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = "M"
slot?: string | null
- staticColor?: 'white' | 'black' | 'auto'
+ staticColor?: 'white' | 'black'
styles?: StylesPropWithHeight
} |
Closes #7471
✅ Pull Request Checklist:
📝 Test Instructions:
Test the RAC Modal example in the docs with Talkback and make sure you can still move focus around via swipes. Will need smoke tests in general to make sure nothing else is broken, note that anything use case that would restore focus if focus escaped focus containment won't work in Android Chrome Talkback
🧢 Your Project:
RSP