diff --git a/src/components/ActivityIndicator.tsx b/src/components/ActivityIndicator.tsx index 8faa54de58..58ef633c9b 100644 --- a/src/components/ActivityIndicator.tsx +++ b/src/components/ActivityIndicator.tsx @@ -146,8 +146,8 @@ const ActivityIndicator = ({ style={[styles.container, style]} {...rest} accessible - accessibilityRole="progressbar" - accessibilityState={{ busy: animating }} + role="progressbar" + aria-busy={animating} > & { /** * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Function to execute on press. */ @@ -79,7 +79,7 @@ const AppbarAction = ({ icon, disabled, onPress, - accessibilityLabel, + 'aria-label': ariaLabel, isLeading, theme: themeOverrides, ref, @@ -101,7 +101,7 @@ const AppbarAction = ({ iconColor={actionIconColor} icon={icon} disabled={disabled} - accessibilityLabel={accessibilityLabel} + aria-label={ariaLabel} animated ref={ref} {...rest} diff --git a/src/components/Appbar/AppbarBackAction.tsx b/src/components/Appbar/AppbarBackAction.tsx index 80c7df2f95..2835c27c91 100644 --- a/src/components/Appbar/AppbarBackAction.tsx +++ b/src/components/Appbar/AppbarBackAction.tsx @@ -31,7 +31,7 @@ export type Props = $Omit< /** * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Function to execute on press. */ @@ -58,12 +58,12 @@ export type Props = $Omit< * ``` */ const AppbarBackAction = ({ - accessibilityLabel = 'Back', + 'aria-label': ariaLabel = 'Back', ref, ...rest }: Props) => ( @@ -162,13 +153,9 @@ const AppbarContent = ({ if (onPress) { return ( - // eslint-disable-next-line react-native-a11y/has-accessibility-props {children} diff --git a/src/components/BottomNavigation/BottomNavigation.tsx b/src/components/BottomNavigation/BottomNavigation.tsx index cd8dbd8663..df7e28cc76 100644 --- a/src/components/BottomNavigation/BottomNavigation.tsx +++ b/src/components/BottomNavigation/BottomNavigation.tsx @@ -23,7 +23,7 @@ export type BaseRoute = { focusedIcon?: IconSource; unfocusedIcon?: IconSource; badge?: string | number | boolean; - accessibilityLabel?: string; + 'aria-label'?: string; testID?: string; lazy?: boolean; }; @@ -77,7 +77,7 @@ export type Props = { * - `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x * - `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3 * - `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text. - * - `accessibilityLabel`: accessibility label for the tab button + * - `aria-label`: accessibility label for the tab button * - `testID`: test id for the tab button * * Example: @@ -164,7 +164,7 @@ export type Props = { renderTouchable?: (props: TouchableProps) => React.ReactNode; /** * Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab. - * Uses `route.accessibilityLabel` by default. + * Uses `route['aria-label']` by default. */ getAccessibilityLabel?: (props: { route: Route }) => string | undefined; /** @@ -523,7 +523,7 @@ const BottomNavigation = ({ = { * - `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x * - `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3 * - `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text. - * - `accessibilityLabel`: accessibility label for the tab button + * - `aria-label`: accessibility label for the tab button * - `testID`: test id for the tab button * * Example: @@ -131,7 +134,7 @@ export type Props = { renderTouchable?: (props: TouchableProps) => React.ReactNode; /** * Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab. - * Uses `route.accessibilityLabel` by default. + * Uses `route['aria-label']` by default. */ getAccessibilityLabel?: (props: { route: Route }) => string | undefined; /** @@ -305,8 +308,7 @@ const BottomNavigationBar = ({ ), getLabelText = ({ route }: { route: Route }) => route.title, getBadge = ({ route }: { route: Route }) => route.badge, - getAccessibilityLabel = ({ route }: { route: Route }) => - route.accessibilityLabel, + getAccessibilityLabel = ({ route }: { route: Route }) => route['aria-label'], getTestID = ({ route }: { route: Route }) => route.testID, activeColor, inactiveColor, @@ -507,7 +509,7 @@ const BottomNavigationBar = ({ maxWidth: maxTabBarWidth, }, ]} - accessibilityRole={'tablist'} + role={'tablist'} testID={`${testID}-content-wrapper`} > {routes.map((route, index) => { @@ -585,9 +587,9 @@ const BottomNavigationBar = ({ onPress: () => onTabPress(eventForIndex(index)), onLongPress: () => onTabLongPress?.(eventForIndex(index)), testID: getTestID({ route }), - accessibilityLabel: getAccessibilityLabel({ route }), - accessibilityRole: Platform.OS === 'ios' ? 'button' : 'tab', - accessibilityState: { selected: focused }, + 'aria-label': getAccessibilityLabel({ route }), + role: Platform.OS === 'ios' ? 'button' : 'tab', + 'aria-selected': focused, style: [styles.item, styles.v3Item], children: ( , 'mode'> & { /** * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Accessibility hint for the button. This is read by the screen reader when the user taps the button. */ @@ -88,7 +88,7 @@ export type Props = $Omit, 'mode'> & { /** * Accessibility role for the button. The "button" role is set by default. */ - accessibilityRole?: AccessibilityRole; + role?: Role; /** * Function to execute on press. */ @@ -169,9 +169,9 @@ const Button = ({ buttonColor: customButtonColor, textColor: customTextColor, children, - accessibilityLabel, + 'aria-label': ariaLabel, accessibilityHint, - accessibilityRole = 'button', + role = 'button', hitSlop, onPress, onPressIn, @@ -352,10 +352,10 @@ const Button = ({ onPressIn={hasPassedTouchHandler ? handlePressIn : undefined} onPressOut={hasPassedTouchHandler ? handlePressOut : undefined} delayLongPress={delayLongPress} - accessibilityLabel={accessibilityLabel} + aria-label={ariaLabel} accessibilityHint={accessibilityHint} - accessibilityRole={accessibilityRole} - accessibilityState={{ disabled }} + role={role} + aria-disabled={disabled} accessible={accessible} hitSlop={hitSlop} disabled={disabled} diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index b893a6530c..f93b48becc 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -247,14 +247,12 @@ const Checkbox = ({ rest.accessible === false ? {} : { - accessibilityRole: 'checkbox' as const, - accessibilityState: { - disabled: !!disabled, - checked: (status === 'indeterminate' - ? 'mixed' - : status === 'checked') as boolean | 'mixed', - }, - accessibilityLiveRegion: 'polite' as const, + role: 'checkbox' as const, + 'aria-disabled': !!disabled, + 'aria-checked': (status === 'indeterminate' + ? 'mixed' + : status === 'checked') as boolean | 'mixed', + 'aria-live': 'polite' as const, }; return ( diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index 50e758a412..e8bd23a59b 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -44,7 +44,7 @@ export type Props = { /** * Accessibility label for the touchable. This is read by the screen reader when the user taps the touchable. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Custom color for unchecked checkbox. */ @@ -129,7 +129,7 @@ const CheckboxItem = ({ theme: themeOverrides, testID, position = 'trailing', - accessibilityLabel = label, + 'aria-label': ariaLabel = label, disabled, labelVariant = 'bodyLarge', labelMaxFontSizeMultiplier = 1.5, @@ -154,12 +154,10 @@ const CheckboxItem = ({ return ( , 'mode'> & { /** * Accessibility label for the chip. This is read by the screen reader when the user taps the chip. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Accessibility label for the close icon. This is read by the screen reader when the user taps the close icon. */ @@ -178,8 +177,8 @@ const Chip = ({ selected = false, disabled = false, background, - accessibilityLabel, - accessibilityRole = 'button', + 'aria-label': ariaLabel, + role = 'button', closeIconAccessibilityLabel = 'Close', onPress, onLongPress, @@ -263,11 +262,6 @@ const Chip = ({ disabled, }); - const accessibilityState: AccessibilityState = { - selected, - disabled, - }; - const elevationStyle = elevation; const multiplier = compact ? 1.5 : 2; const labelSpacings = { @@ -312,9 +306,10 @@ const Chip = ({ onPressOut={hasPassedTouchHandler ? handlePressOut : undefined} delayLongPress={delayLongPress} disabled={disabled} - accessibilityLabel={accessibilityLabel} - accessibilityRole={accessibilityRole} - accessibilityState={accessibilityState} + aria-label={ariaLabel} + role={role} + aria-selected={selected} + aria-disabled={disabled} testID={testID} theme={theme} hitSlop={hitSlop} @@ -401,8 +396,8 @@ const Chip = ({ {closeIcon ? ( diff --git a/src/components/DataTable/DataTablePagination.tsx b/src/components/DataTable/DataTablePagination.tsx index 862185e9c5..32f4b8b4fb 100644 --- a/src/components/DataTable/DataTablePagination.tsx +++ b/src/components/DataTable/DataTablePagination.tsx @@ -29,7 +29,7 @@ export type Props = ViewProps & /** * AccessibilityLabel for `label`. */ - accessibilityLabel?: string; + 'aria-label'?: string; style?: StyleProp; /** * @optional @@ -106,7 +106,7 @@ const PaginationControls = ({ iconColor={textColor} disabled={page === 0} onPress={() => onPageChange(0)} - accessibilityLabel="page-first" + aria-label="page-first" theme={theme} /> ) : null} @@ -122,7 +122,7 @@ const PaginationControls = ({ iconColor={textColor} disabled={page === 0} onPress={() => onPageChange(page - 1)} - accessibilityLabel="chevron-left" + aria-label="chevron-left" theme={theme} /> onPageChange(page + 1)} - accessibilityLabel="chevron-right" + aria-label="chevron-right" theme={theme} /> {showFastPaginationControls ? ( @@ -153,7 +153,7 @@ const PaginationControls = ({ iconColor={textColor} disabled={numberOfPages === 0 || page === numberOfPages - 1} onPress={() => onPageChange(numberOfPages - 1)} - accessibilityLabel="page-last" + aria-label="page-last" theme={theme} /> ) : null} @@ -266,7 +266,7 @@ const PaginationDropdown = ({ */ const DataTablePagination = ({ label, - accessibilityLabel, + 'aria-label': accessibilityLabel, page, numberOfPages, onPageChange, @@ -287,19 +287,21 @@ const DataTablePagination = ({ {numberOfItemsPerPageList && numberOfItemsPerPage && onItemsPerPageChange && ( {label} diff --git a/src/components/Dialog/DialogTitle.tsx b/src/components/Dialog/DialogTitle.tsx index f869887205..bdf5021379 100644 --- a/src/components/Dialog/DialogTitle.tsx +++ b/src/components/Dialog/DialogTitle.tsx @@ -63,7 +63,7 @@ const DialogTitle = ({ return ( diff --git a/src/components/Drawer/DrawerCollapsedItem.tsx b/src/components/Drawer/DrawerCollapsedItem.tsx index 3f79fce421..1542eafe36 100644 --- a/src/components/Drawer/DrawerCollapsedItem.tsx +++ b/src/components/Drawer/DrawerCollapsedItem.tsx @@ -54,7 +54,7 @@ export type Props = ViewProps & { /** * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; style?: StyleProp; /** * @optional @@ -102,7 +102,7 @@ const DrawerCollapsedItem = ({ style, onPress, disabled, - accessibilityLabel, + 'aria-label': ariaLabel, badge = false, testID = 'drawer-collapsed-item', labelMaxFontSizeMultiplier, @@ -164,17 +164,13 @@ const DrawerCollapsedItem = ({ return ( - {/* eslint-disable-next-line react-native-a11y/has-accessibility-props */} diff --git a/src/components/Drawer/DrawerItem.tsx b/src/components/Drawer/DrawerItem.tsx index 95ef989248..88535ba162 100644 --- a/src/components/Drawer/DrawerItem.tsx +++ b/src/components/Drawer/DrawerItem.tsx @@ -46,7 +46,7 @@ export type Props = ViewProps & { /** * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Callback which returns a React element to display on the right side. For instance a Badge. */ @@ -94,7 +94,7 @@ const DrawerItem = ({ style, onPress, background, - accessibilityLabel, + 'aria-label': ariaLabel, right, labelMaxFontSizeMultiplier, hitSlop, @@ -124,9 +124,9 @@ const DrawerItem = ({ { backgroundColor, borderRadius }, style, ]} - accessibilityRole="button" - accessibilityState={{ selected: active }} - accessibilityLabel={accessibilityLabel} + role="button" + aria-selected={active} + aria-label={ariaLabel} theme={theme} hitSlop={hitSlop} > diff --git a/src/components/FAB/Extended.tsx b/src/components/FAB/Extended.tsx index d15fe6a6fa..06a67cab1f 100644 --- a/src/components/FAB/Extended.tsx +++ b/src/components/FAB/Extended.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { StyleSheet, View } from 'react-native'; import type { - AccessibilityState, ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, @@ -71,11 +70,24 @@ export type Props = { /** * Accessibility label. Falls back to `label` if unset. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** - * Accessibility state forwarded to the underlying button. + * Indicates whether the element is checked. Accepts `true`, `false`, + * or `'mixed'` for an indeterminate state. */ - accessibilityState?: AccessibilityState; + 'aria-checked'?: boolean | 'mixed'; + /** + * Indicates whether the element is selected. + */ + 'aria-selected'?: boolean; + /** + * Indicates whether the element is currently busy (e.g. loading). + */ + 'aria-busy'?: boolean; + /** + * Indicates whether the element's controlled content is expanded. + */ + 'aria-expanded'?: boolean; /** * Specifies the largest possible scale a label font can reach. */ @@ -148,8 +160,11 @@ const Extended = ({ expanded, visible = true, onPress, - accessibilityLabel = label, - accessibilityState, + 'aria-label': ariaLabel = label, + 'aria-checked': ariaChecked, + 'aria-selected': ariaSelected, + 'aria-busy': ariaBusy, + 'aria-expanded': ariaExpanded, labelMaxFontSizeMultiplier, background, style, @@ -239,8 +254,11 @@ const Extended = ({ size={size} visible={visible} onPress={onPress} - accessibilityLabel={accessibilityLabel} - accessibilityState={accessibilityState} + aria-label={ariaLabel} + aria-checked={ariaChecked} + aria-selected={ariaSelected} + aria-busy={ariaBusy} + aria-expanded={ariaExpanded} background={background} widthShared={widthValue} labelMaxFontSizeMultiplier={labelMaxFontSizeMultiplier} @@ -253,7 +271,7 @@ const Extended = ({ ref={offscreenLabelRef} style={styles.offscreenMeasure} importantForAccessibility="no-hide-descendants" - accessibilityElementsHidden + aria-hidden > void; - accessibilityLabel?: string; + /** + * Accessibility label for the trigger FAB. + */ + 'aria-label'?: string; testID?: string; }; @@ -206,7 +209,7 @@ const AnimatedItem = ({ expanded ? styles.pointerEventsAuto : styles.pointerEventsNone, ]} importantForAccessibility={expanded ? 'yes' : 'no-hide-descendants'} - accessibilityElementsHidden={!expanded} + aria-hidden={!expanded} > {children} @@ -219,7 +222,10 @@ type ItemProps = { variant: Variant; theme: InternalTheme; onPress: (e: GestureResponderEvent) => void; - accessibilityLabel?: string; + /** + * Accessibility label. Falls back to `label`. + */ + 'aria-label'?: string; testID?: string; }; @@ -235,7 +241,7 @@ const MenuItem = ({ variant, theme, onPress, - accessibilityLabel, + 'aria-label': ariaLabel, testID, }: ItemProps) => { const colors = resolveColors({ theme, variant }); @@ -261,8 +267,8 @@ const MenuItem = ({ onPress={onPress} onFocus={onFocus} onBlur={onBlur} - accessibilityRole="button" - accessibilityLabel={accessibilityLabel ?? label} + role="button" + aria-label={ariaLabel ?? label} style={[ { borderRadius }, Platform.OS === 'web' ? webNoOutline : null, @@ -309,7 +315,10 @@ type MorphingTriggerProps = { visible: boolean; alignment: 'start' | 'center' | 'end'; onPress?: (e: GestureResponderEvent) => void; - accessibilityLabel?: string; + /** + * Accessibility label for the trigger button. + */ + 'aria-label'?: string; theme: InternalTheme; testID?: string; }; @@ -326,7 +335,7 @@ const MorphingTrigger = ({ visible, alignment, onPress, - accessibilityLabel, + 'aria-label': ariaLabel, theme, testID, }: MorphingTriggerProps) => { @@ -446,7 +455,7 @@ const MorphingTrigger = ({ contentColor={triggerContentColor} visible={visible} onPress={onPress} - accessibilityLabel={accessibilityLabel} + aria-label={ariaLabel} widthShared={widthShared} heightShared={heightShared} borderRadiusShared={borderRadiusShared} @@ -616,7 +625,7 @@ const Menu = ({ label={item.label} variant={itemsVariant} theme={theme} - accessibilityLabel={item.accessibilityLabel ?? item.label} + aria-label={item['aria-label'] ?? item.label} onPress={handleItemPress(item)} testID={item.testID} /> @@ -636,7 +645,7 @@ const Menu = ({ visible={triggerVisible} alignment={alignment} onPress={effectiveExpanded ? onDismiss : openOnPress} - accessibilityLabel={trigger.accessibilityLabel} + aria-label={trigger['aria-label']} theme={theme} testID={trigger.testID} /> diff --git a/src/components/FAB/Shell.tsx b/src/components/FAB/Shell.tsx index 4f9b9b5da9..2d2d60e4cd 100644 --- a/src/components/FAB/Shell.tsx +++ b/src/components/FAB/Shell.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { Platform, StyleSheet, View } from 'react-native'; import type { - AccessibilityState, ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, @@ -92,11 +91,24 @@ export type ShellProps = { /** * Accessibility label. Falls back to `label` if unset. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** - * Accessibility state forwarded to the underlying button. + * Indicates whether the element is checked. Accepts `true`, `false`, + * or `'mixed'` for an indeterminate state. */ - accessibilityState?: AccessibilityState; + 'aria-checked'?: boolean | 'mixed'; + /** + * Indicates whether the element is selected. + */ + 'aria-selected'?: boolean; + /** + * Indicates whether the element is currently busy (e.g. loading). + */ + 'aria-busy'?: boolean; + /** + * Indicates whether the element's controlled content is expanded. + */ + 'aria-expanded'?: boolean; /** * Largest scale the label font can reach (auto-built content only). */ @@ -182,8 +194,11 @@ const Shell = ({ elevation = Tokens.stateElevation.enabled, visible = true, onPress, - accessibilityLabel = label, - accessibilityState, + 'aria-label': ariaLabel = label, + 'aria-checked': ariaChecked, + 'aria-selected': ariaSelected, + 'aria-busy': ariaBusy, + 'aria-expanded': ariaExpanded, labelMaxFontSizeMultiplier, labelAnimatedStyle, background, @@ -290,9 +305,12 @@ const Shell = ({ onPress={onPress} onFocus={onFocus} onBlur={onBlur} - accessibilityLabel={accessibilityLabel} - accessibilityRole="button" - accessibilityState={accessibilityState} + aria-label={ariaLabel} + role="button" + aria-checked={ariaChecked} + aria-selected={ariaSelected} + aria-busy={ariaBusy} + aria-expanded={ariaExpanded} testID={testID} style={[ children ? styles.fill : null, diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index 899d1e5a46..52a64e98d0 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -59,7 +59,7 @@ export type Props = Omit<$RemoveChildren, 'style'> & { /** * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Style of button's inner content. * Use this prop to apply custom height and width or to set a custom padding`. @@ -112,7 +112,7 @@ const IconButton = ({ iconColor: customIconColor, containerColor: customContainerColor, size = 24, - accessibilityLabel, + 'aria-label': ariaLabel, disabled, onPress, selected = false, @@ -188,13 +188,10 @@ const IconButton = ({ borderless centered onPress={onPress} - accessibilityLabel={accessibilityLabel} + aria-label={ariaLabel} style={[styles.touchable, contentStyle]} - // @ts-expect-error We keep old a11y props for backwards compat with old RN versions - accessibilityTraits={disabled ? ['button', 'disabled'] : 'button'} - accessibilityComponentType="button" - accessibilityRole="button" - accessibilityState={{ disabled }} + role="button" + aria-disabled={disabled} disabled={disabled} hitSlop={ TouchableRipple.supported diff --git a/src/components/List/ListAccordion.tsx b/src/components/List/ListAccordion.tsx index 5dbca2ee60..97730d58e6 100644 --- a/src/components/List/ListAccordion.tsx +++ b/src/components/List/ListAccordion.tsx @@ -120,7 +120,7 @@ export type Props = { /** * Accessibility label for the TouchableRipple. This is read by the screen reader when the user taps the touchable. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * `pointerEvents` passed to the `View` container */ @@ -190,7 +190,7 @@ const ListAccordion = ({ onLongPress, delayLongPress, expanded: expandedProp, - accessibilityLabel, + 'aria-label': ariaLabel, pointerEvents = 'none', titleMaxFontSizeMultiplier, descriptionMaxFontSizeMultiplier, @@ -249,9 +249,9 @@ const ListAccordion = ({ onPress={handlePress} onLongPress={onLongPress} delayLongPress={delayLongPress} - accessibilityRole="button" - accessibilityState={{ expanded: isExpanded }} - accessibilityLabel={accessibilityLabel} + role="button" + aria-expanded={isExpanded} + aria-label={ariaLabel} testID={testID} theme={theme} background={background} diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index a23fe82942..ca6e849e3d 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -640,8 +640,8 @@ const Menu = ({ {rendered ? ( diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index a8412da806..3c66c885b3 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -182,15 +182,15 @@ function Modal({ return ( diff --git a/src/components/RadioButton/RadioButtonAndroid.tsx b/src/components/RadioButton/RadioButtonAndroid.tsx index a907b46b09..5fcaf9f6e5 100644 --- a/src/components/RadioButton/RadioButtonAndroid.tsx +++ b/src/components/RadioButton/RadioButtonAndroid.tsx @@ -134,9 +134,10 @@ const RadioButtonAndroid = ({ }); } } - accessibilityRole="radio" - accessibilityState={{ disabled, checked }} - accessibilityLiveRegion="polite" + role="radio" + aria-disabled={disabled} + aria-checked={checked} + aria-live="polite" style={styles.container} testID={testID} theme={theme} diff --git a/src/components/RadioButton/RadioButtonGroup.tsx b/src/components/RadioButton/RadioButtonGroup.tsx index 2bdd596561..8522bf3fc4 100644 --- a/src/components/RadioButton/RadioButtonGroup.tsx +++ b/src/components/RadioButton/RadioButtonGroup.tsx @@ -56,7 +56,7 @@ export const RadioButtonContext = React.createContext( */ const RadioButtonGroup = ({ value, onValueChange, children }: Props) => ( - {children} + {children} ); diff --git a/src/components/RadioButton/RadioButtonIOS.tsx b/src/components/RadioButton/RadioButtonIOS.tsx index e2e5f06c7c..015356eea3 100644 --- a/src/components/RadioButton/RadioButtonIOS.tsx +++ b/src/components/RadioButton/RadioButtonIOS.tsx @@ -92,9 +92,10 @@ const RadioButtonIOS = ({ }); } } - accessibilityRole="radio" - accessibilityState={{ disabled, checked }} - accessibilityLiveRegion="polite" + role="radio" + aria-disabled={disabled} + aria-checked={checked} + aria-live="polite" style={styles.container} testID={testID} theme={theme} diff --git a/src/components/RadioButton/RadioButtonItem.tsx b/src/components/RadioButton/RadioButtonItem.tsx index 1591760980..e37ed435e2 100644 --- a/src/components/RadioButton/RadioButtonItem.tsx +++ b/src/components/RadioButton/RadioButtonItem.tsx @@ -49,7 +49,7 @@ export type Props = { /** * Accessibility label for the touchable. This is read by the screen reader when the user taps the touchable. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Custom color for unchecked radio. */ @@ -149,7 +149,7 @@ const RadioButtonItem = ({ status, theme: themeOverrides, background, - accessibilityLabel = label, + 'aria-label': ariaLabel = label, testID, mode, position = 'trailing', @@ -204,12 +204,10 @@ const RadioButtonItem = ({ }) } onLongPress={onLongPress} - accessibilityLabel={accessibilityLabel} - accessibilityRole="radio" - accessibilityState={{ - checked, - disabled, - }} + aria-label={ariaLabel} + role="radio" + aria-checked={checked} + aria-disabled={disabled} testID={testID} disabled={disabled} background={background} diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index a081b62fc4..3cc733e126 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -245,7 +245,7 @@ const Searchbar = ({ theme={theme} > )} {shouldRenderTraileringIcon ? ( ) : null} diff --git a/src/components/SegmentedButtons/SegmentedButtonItem.tsx b/src/components/SegmentedButtons/SegmentedButtonItem.tsx index 0d30d8aee4..4f701f0b48 100644 --- a/src/components/SegmentedButtons/SegmentedButtonItem.tsx +++ b/src/components/SegmentedButtons/SegmentedButtonItem.tsx @@ -52,7 +52,7 @@ export type Props = { /** * Accessibility label for the `SegmentedButtonItem`. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Function to execute on press. */ @@ -102,7 +102,7 @@ export type Props = { const SegmentedButtonItem = ({ checked, - accessibilityLabel, + 'aria-label': ariaLabel, disabled, style, labelStyle, @@ -195,9 +195,10 @@ const SegmentedButtonItem = ({ = { * - `value`: value of button (required) * - `icon`: icon to display for the item * - `disabled`: whether the button is disabled - * - `accessibilityLabel`: acccessibility label for the button. This is read by the screen reader when the user taps the button. + * - `aria-label`: accessibility label for the button. This is read by the screen reader when the user taps the button. * - `checkedColor`: custom color for checked Text and Icon * - `uncheckedColor`: custom color for unchecked Text and Icon * - `onPress`: callback that is called when button is pressed @@ -62,7 +62,7 @@ export type Props = { value: T; icon?: IconSource; disabled?: boolean; - accessibilityLabel?: string; + 'aria-label'?: string; checkedColor?: string; uncheckedColor?: string; onPress?: (event: GestureResponderEvent) => void; diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index 2d6ee23211..04502eb7de 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -284,7 +284,7 @@ const Snackbar = ({ > diff --git a/src/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx index fd145bfdee..6f12dafdd8 100644 --- a/src/components/Switch/Switch.tsx +++ b/src/components/Switch/Switch.tsx @@ -57,7 +57,10 @@ export type Props = { style?: StyleProp; testID?: string; theme?: ThemeProp; - accessibilityLabel?: string; + /** + * Accessibility label for the switch. This is read by the screen reader when the user focuses the switch. + */ + 'aria-label'?: string; }; const { @@ -132,7 +135,7 @@ const Switch = ({ style, testID, theme: themeOverrides, - accessibilityLabel, + 'aria-label': ariaLabel, }: Props) => { const theme = useInternalTheme(themeOverrides); const reduceMotion = useReduceMotion(); @@ -367,9 +370,10 @@ const Switch = ({ focusedSV.value = 0; }} android_ripple={{ color: 'transparent' }} - accessibilityRole="switch" - accessibilityState={{ disabled: isDisabled, checked }} - accessibilityLabel={accessibilityLabel} + role="switch" + aria-disabled={isDisabled} + aria-checked={checked} + aria-label={ariaLabel} testID={testID} style={[ styles.touchable, diff --git a/src/components/TextInput/TextInput.tsx b/src/components/TextInput/TextInput.tsx index 806dd4585a..5a7a65d974 100644 --- a/src/components/TextInput/TextInput.tsx +++ b/src/components/TextInput/TextInput.tsx @@ -57,7 +57,7 @@ export type TextInputColors = { }; export type GetAccessibilityDataReturn = { - input: AccessibilityProps; + input: AccessibilityProps & { 'aria-invalid'?: boolean }; supportingText: AccessibilityProps; counter: AccessibilityProps; }; diff --git a/src/components/TextInput/utils.ts b/src/components/TextInput/utils.ts index e8da1a4de3..ad81f55301 100644 --- a/src/components/TextInput/utils.ts +++ b/src/components/TextInput/utils.ts @@ -611,19 +611,14 @@ export const getAccessibilityData = ({ : `Characters entered ${inputLength} of ${maxLength}` : undefined; - const accessibilityState = { - disabled: isDisabled, - invalid: isInvalid, - ...props.accessibilityState, - } as const; - return { input: { 'aria-label': ariaLabel, 'aria-valuemax': isCounterReached ? maxLength : undefined, 'aria-valuenow': isCounterReached ? inputLength : undefined, + 'aria-disabled': isDisabled, + 'aria-invalid': isInvalid, accessibilityHint: hint, - accessibilityState, }, supportingText: { 'aria-hidden': isSupportingTextHidden, diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx index 8d4cdd556f..22598c16b2 100644 --- a/src/components/ToggleButton/ToggleButton.tsx +++ b/src/components/ToggleButton/ToggleButton.tsx @@ -29,7 +29,7 @@ export type Props = { /** * Accessibility label for the `ToggleButton`. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Function to execute on press. */ @@ -88,7 +88,7 @@ const ToggleButton = ({ icon, size, theme: themeOverrides, - accessibilityLabel, + 'aria-label': ariaLabel, disabled, style, value, @@ -123,8 +123,9 @@ const ToggleButton = ({ } }} size={size} - accessibilityLabel={accessibilityLabel} - accessibilityState={{ disabled, selected: checked }} + aria-label={ariaLabel} + aria-disabled={disabled} + aria-selected={checked} disabled={disabled} style={[ styles.content, diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index 36dc08971b..8d237d6991 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -215,7 +215,7 @@ const Tooltip = ({ testID="tooltip-container" > { ); }); - it('Is recognized as a header when no onPress callback has been pressed', async () => { + it('Is recognized as a heading when no onPress callback has been passed', async () => { await render( @@ -162,7 +162,7 @@ describe('renderAppbarContent', () => { ); - expect(screen.getByRole('header')).toBeOnTheScreen(); + expect(screen.getByRole('heading')).toBeOnTheScreen(); }); it('is recognized as a button when onPress callback has been passed', async () => { await render( @@ -174,7 +174,7 @@ describe('renderAppbarContent', () => { ); expect(screen.getByRole('button')).toBeEnabled(); - expect(screen.queryByRole('header')).not.toBeOnTheScreen(); + expect(screen.queryByRole('heading')).not.toBeOnTheScreen(); }); it('is recognized as a disabled button when onPress and disabled is passed', async () => { await render( @@ -186,7 +186,7 @@ describe('renderAppbarContent', () => { ); expect(screen.getByRole('button')).toBeDisabled(); - expect(screen.queryByRole('header')).not.toBeOnTheScreen(); + expect(screen.queryByRole('heading')).not.toBeOnTheScreen(); }); }); diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index 7dbcf9f0a5..a5d9d95766 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -122,9 +122,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` testID="search-bar-icon-container" > { ); diff --git a/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap index 1b21c11b68..54f2e4f7a4 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap @@ -3,7 +3,6 @@ exports[`renders Checkbox with custom testID 1`] = ` { expect(tree).toMatchSnapshot(); }); +it('renders FAB with aria-label', async () => { + const tree = ( + await render() + ).toJSON(); + expect(tree).toMatchSnapshot(); +}); + it('renders FAB medium size', async () => { const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); @@ -64,13 +71,6 @@ it('renders FAB with containerColor and contentColor overrides', async () => { expect(tree).toMatchSnapshot(); }); -it('renders FAB with accessibilityLabel', async () => { - const tree = ( - await render() - ).toJSON(); - expect(tree).toMatchSnapshot(); -}); - it('renders FAB transitioning to not visible', async () => { const { rerender, toJSON } = await render(); await rerender(); @@ -86,15 +86,18 @@ it('renders FAB transitioning to visible', async () => { }); it('calls onPress when FAB is pressed', async () => { + const user = userEvent.setup(); const onPress = jest.fn(); - await render(); - await userEvent.press(screen.getByTestId('fab')); + await render(); + await user.press(screen.getByRole('button', { name: 'Add item' })); expect(onPress).toHaveBeenCalledTimes(1); }); it('forwards event object to onPress', async () => { const onPress = jest.fn(); - await render(); - await fireEvent(screen.getByTestId('fab'), 'onPress', { key: 'value' }); + await render(); + await fireEvent(screen.getByRole('button', { name: 'Add item' }), 'onPress', { + key: 'value', + }); expect(onPress).toHaveBeenCalledWith({ key: 'value' }); }); diff --git a/src/components/__tests__/FABExtended.test.tsx b/src/components/__tests__/FABExtended.test.tsx index 2d24e0a038..fd9a21cec9 100644 --- a/src/components/__tests__/FABExtended.test.tsx +++ b/src/components/__tests__/FABExtended.test.tsx @@ -57,7 +57,7 @@ it('renders extended FAB transitioning to collapsed', async () => { expect(toJSON()).toMatchSnapshot(); }); -it('uses label as default accessibilityLabel', async () => { +it('uses label as default aria-label', async () => { await render( { expect(screen.getByLabelText('New message')).toBeOnTheScreen(); }); -it('respects explicit accessibilityLabel', async () => { +it('respects explicit aria-label', async () => { await render( ); @@ -85,6 +85,7 @@ it('respects explicit accessibilityLabel', async () => { }); it('calls onPress when pressed', async () => { + const user = userEvent.setup(); const onPress = jest.fn(); await render( { testID="extended-fab" /> ); - await userEvent.press(screen.getByTestId('extended-fab')); + await user.press(screen.getByRole('button', { name: 'New message' })); expect(onPress).toHaveBeenCalledTimes(1); }); @@ -110,8 +111,12 @@ it('forwards event object to onPress', async () => { testID="extended-fab" /> ); - await fireEvent(screen.getByTestId('extended-fab'), 'onPress', { - key: 'value', - }); + await fireEvent( + screen.getByRole('button', { name: 'New message' }), + 'onPress', + { + key: 'value', + } + ); expect(onPress).toHaveBeenCalledWith({ key: 'value' }); }); diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index e4f683fa52..ba66e705a7 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -53,21 +53,13 @@ describe('Menu Item', () => { ).toBe(labelMaxFontSizeMultiplier); }); - it('accepts different values for accessibilityState', async () => { - await render( - - ); + it('accepts aria-checked prop', async () => { + await render(); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('touchable').props.accessibilityState - ).toMatchObject({ - checked: true, - }); + expect(screen.getByRole('menuitem')).toHaveProp( + 'accessibilityState', + expect.objectContaining({ checked: true }) + ); }); }); diff --git a/src/components/__tests__/ProgressBar.test.tsx b/src/components/__tests__/ProgressBar.test.tsx index db2a2bf600..4939f25c4c 100644 --- a/src/components/__tests__/ProgressBar.test.tsx +++ b/src/components/__tests__/ProgressBar.test.tsx @@ -86,6 +86,12 @@ it('renders progress bar with full height on web', async () => { }); }); +it('has progressbar role', async () => { + await render(); + + expect(screen.getByRole(a11yRole)).toBeOnTheScreen(); +}); + it('renders progress bar with custom style of filled part', async () => { await render( diff --git a/src/components/__tests__/RadioButton/__snapshots__/RadioButton.test.tsx.snap b/src/components/__tests__/RadioButton/__snapshots__/RadioButton.test.tsx.snap index 5560a80f36..c20910f20e 100644 --- a/src/components/__tests__/RadioButton/__snapshots__/RadioButton.test.tsx.snap +++ b/src/components/__tests__/RadioButton/__snapshots__/RadioButton.test.tsx.snap @@ -3,7 +3,6 @@ exports[`RadioButton RadioButton with custom testID renders properly 1`] = ` { }); }); +describe('Switch accessibility', () => { + it('has switch role', async () => { + await render(); + + expect(screen.getByRole('switch')).toBeOnTheScreen(); + }); +}); + describe('Switch interaction', () => { it('toggles to true when off and pressed', async () => { + const user = userEvent.setup(); const onValueChange = jest.fn(); await render(); - await userEvent.press(screen.getByRole('switch')); + await user.press(screen.getByRole('switch')); expect(onValueChange).toHaveBeenCalledWith(true); }); it('toggles to false when on and pressed', async () => { + const user = userEvent.setup(); const onValueChange = jest.fn(); await render(); - await userEvent.press(screen.getByRole('switch')); + await user.press(screen.getByRole('switch')); expect(onValueChange).toHaveBeenCalledWith(false); }); it('does not fire onValueChange when disabled', async () => { + const user = userEvent.setup(); const onValueChange = jest.fn(); await render( ); - await userEvent.press(screen.getByRole('switch')); + await user.press(screen.getByRole('switch')); expect(onValueChange).not.toHaveBeenCalled(); }); }); diff --git a/src/components/__tests__/TextInput.test.tsx b/src/components/__tests__/TextInput.test.tsx index 6a0fe19768..14b1c72bdb 100644 --- a/src/components/__tests__/TextInput.test.tsx +++ b/src/components/__tests__/TextInput.test.tsx @@ -1,5 +1,10 @@ import * as React from 'react'; -import { I18nManager, TextInput as NativeTextInput, View } from 'react-native'; +import { + I18nManager, + StyleSheet, + TextInput as NativeTextInput, + View, +} from 'react-native'; import type { GestureResponderEvent } from 'react-native'; import { @@ -23,6 +28,13 @@ import type { TextInputAccessoryProps } from '../TextInput/TextInputIcon'; const stateOpacity = tokens.md.sys.state.opacity; +const styles = StyleSheet.create({ + textInputStyle: { + fontSize: 40, + letterSpacing: 9, + }, +}); + const defaultI18nIsRTL = I18nManager.isRTL; const includeHiddenElements = { includeHiddenElements: true }; @@ -181,6 +193,7 @@ it('renders outlined TextInput with TextInput.Icon accessories when error is tru }); it('fires onPress on TextInput.Icon end accessory', async () => { + const user = userEvent.setup(); const onClear = jest.fn<(event: GestureResponderEvent) => void>(); await render( { {...props} icon="close" onPress={onClear} - accessibilityLabel="Clear" + aria-label="Clear" /> )} /> ); - await userEvent.press(screen.getAllByTestId('icon-button')[1]); + await user.press(screen.getByRole('button', { name: 'Clear' })); expect(onClear).toHaveBeenCalledTimes(1); }); @@ -222,11 +235,9 @@ it('disables TextInput.Icon when the field is disabled', async () => { /> ); - const buttons = screen.getAllByTestId('icon-button'); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(buttons[0].props.accessibilityState?.disabled).toBe(true); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(buttons[1].props.accessibilityState?.disabled).toBe(true); + const buttons = screen.getAllByRole('button'); + expect(buttons[0]).toBeDisabled(); + expect(buttons[1]).toBeDisabled(); }); it('does not disable TextInput.Icon when the field is read-only (editable false)', async () => { @@ -245,11 +256,9 @@ it('does not disable TextInput.Icon when the field is read-only (editable false) /> ); - const buttons = screen.getAllByTestId('icon-button'); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(buttons[0].props.accessibilityState?.disabled).not.toBe(true); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(buttons[1].props.accessibilityState?.disabled).not.toBe(true); + const buttons = screen.getAllByRole('button'); + expect(buttons[0]).toBeEnabled(); + expect(buttons[1]).toBeEnabled(); }); it('renders supporting text below the field', async () => { @@ -279,12 +288,8 @@ it('uses polite aria-live on error supporting text', async () => { /> ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(screen.getByText('Invalid').props['aria-live']).toBe('polite'); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(screen.getByTestId('tf-input').props.accessibilityState?.invalid).toBe( - true - ); + expect(screen.getByText('Invalid')).toHaveProp('aria-live', 'polite'); + expect(screen.getByLabelText('Email')).toHaveProp('aria-invalid', true); }); it('marks the input invalid when error is true without supporting text', async () => { @@ -298,14 +303,9 @@ it('marks the input invalid when error is true without supporting text', async ( /> ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(screen.getByTestId('tf-input').props.accessibilityState?.invalid).toBe( - true - ); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('tf-input').props.accessibilityHint - ).toBeUndefined(); + const input = screen.getByLabelText('Email'); + expect(input).toHaveProp('aria-invalid', true); + expect(input).not.toHaveProp('accessibilityHint'); }); it('hides helper supporting text from the accessibility tree and omits aria-live', async () => { @@ -319,18 +319,10 @@ it('hides helper supporting text from the accessibility tree and omits aria-live /> ); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByText('Optional', includeHiddenElements).props['aria-hidden'] - ).toBe(true); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByText('Optional', includeHiddenElements).props['aria-live'] - ).toBeUndefined(); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(screen.getByTestId('tf-input').props['aria-label']).toBe( - 'Email, Optional' - ); + const supportingText = screen.getByText('Optional', includeHiddenElements); + expect(supportingText).toHaveProp('aria-hidden', true); + expect(supportingText).not.toHaveProp('aria-live'); + expect(screen.getByLabelText('Email, Optional')).toBeOnTheScreen(); }); it('includes supporting text in aria-label when label is omitted', async () => { @@ -343,10 +335,7 @@ it('includes supporting text in aria-label when label is omitted', async () => { /> ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(screen.getByTestId('tf-input').props['aria-label']).toBe( - 'Helper only' - ); + expect(screen.getByLabelText('Helper only')).toBeOnTheScreen(); }); it('does not mark the input as aria-disabled when editable is false (read-only)', async () => { @@ -360,13 +349,10 @@ it('does not mark the input as aria-disabled when editable is false (read-only)' /> ); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('tf-input').props.accessibilityState?.disabled - ).not.toBe(true); + expect(screen.getByLabelText('Email')).toHaveProp('aria-disabled', false); }); -it('marks the input as disabled in accessibilityState when disabled is true', async () => { +it('marks the input as aria-disabled when disabled is true', async () => { await render( ); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('tf-input').props.accessibilityState?.disabled - ).toBe(true); + expect(screen.getByLabelText('Email')).toBeDisabled(); }); it('renders the input via render with merged props', async () => { @@ -624,6 +607,7 @@ it('invokes onFocus and onBlur on the TextInput', async () => { }); it('focuses the TextInput when the outer Pressable is pressed', async () => { + const user = userEvent.setup(); const focusSpy = jest.spyOn(NativeTextInput.prototype, 'focus'); const { root } = await render( @@ -637,26 +621,28 @@ it('focuses the TextInput when the outer Pressable is pressed', async () => { expect(screen.getByTestId('tf-input')).toBeOnTheScreen(); - await userEvent.press(getOuterTextInputPressable(root)); + await user.press(getOuterTextInputPressable(root)); expect(focusSpy).toHaveBeenCalled(); focusSpy.mockRestore(); }); it('does not focus the TextInput when disabled and the Pressable is pressed', async () => { + const user = userEvent.setup(); const focusSpy = jest.spyOn(NativeTextInput.prototype, 'focus'); const { root } = await render( {}} disabled /> ); - await userEvent.press(getOuterTextInputPressable(root)); + await user.press(getOuterTextInputPressable(root)); expect(focusSpy).not.toHaveBeenCalled(); focusSpy.mockRestore(); }); it('focuses the TextInput when read-only and the Pressable is pressed', async () => { + const user = userEvent.setup(); const focusSpy = jest.spyOn(NativeTextInput.prototype, 'focus'); const { root } = await render( @@ -668,7 +654,7 @@ it('focuses the TextInput when read-only and the Pressable is pressed', async () /> ); - await userEvent.press(getOuterTextInputPressable(root)); + await user.press(getOuterTextInputPressable(root)); expect(focusSpy).toHaveBeenCalled(); focusSpy.mockRestore(); @@ -1182,7 +1168,7 @@ it('does not apply the TextInput style prop to prefix or suffix Text', async () onChangeText={() => {}} prefix="$" suffix="]" - style={{ fontSize: 40, letterSpacing: 9 }} + style={styles.textInputStyle} testID="tf-input-style" /> ); @@ -1217,6 +1203,7 @@ it('passes defaultValue to the native input when uncontrolled without counter', it('updates the character counter for an uncontrolled field with counter enabled', async () => { const onChangeText = jest.fn(); + const user = userEvent.setup(); await render(