mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
fix(a11y): TextInput aria-label handling (#53051)
Summary: The `aria-label` prop was ignored on `TextInput` component. Which resulted in screen reader not able to read it. This PR forwards `aria-label` to `accessibilityLabel` in a manner similar to e.g. `View` and `Text` ## Changelog: <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> [GENERAL] [FIXED] - a11y: fix `aria-label` on `TextInput` Pull Request resolved: https://github.com/facebook/react-native/pull/53051 Test Plan: Run RNTester => TextInput => Accessibility section under Accessibility Inspector or screen reader. iOS fixed: https://github.com/user-attachments/assets/68c3a2ef-7dfe-479c-97fc-cbe72108c45c iOS baseline: https://github.com/user-attachments/assets/2e8372ba-10dc-47d2-b6b1-9f664000de7d Reviewed By: andrewdacenko Differential Revision: D79635413 Pulled By: rshest fbshipit-source-id: dd2f583d67c6c6c6393e02c5fe534308e1e2f921
This commit is contained in:
committed by
React Native Bot
parent
4c781e4fbd
commit
1ca723220d
@@ -618,6 +618,9 @@ function InternalTextInput(props: TextInputProps): React.Node {
|
||||
// so omitting onBlur and onFocus pressability handlers here.
|
||||
const {onBlur, onFocus, ...eventHandlers} = usePressability(config);
|
||||
|
||||
const _accessibilityLabel =
|
||||
props?.['aria-label'] ?? props?.accessibilityLabel;
|
||||
|
||||
let _accessibilityState;
|
||||
if (
|
||||
accessibilityState != null ||
|
||||
@@ -681,6 +684,7 @@ function InternalTextInput(props: TextInputProps): React.Node {
|
||||
{...otherProps}
|
||||
{...eventHandlers}
|
||||
acceptDragAndDropTypes={props.experimental_acceptDragAndDropTypes}
|
||||
accessibilityLabel={_accessibilityLabel}
|
||||
accessibilityState={_accessibilityState}
|
||||
accessible={accessible}
|
||||
submitBehavior={submitBehavior}
|
||||
@@ -744,8 +748,9 @@ function InternalTextInput(props: TextInputProps): React.Node {
|
||||
{...otherProps}
|
||||
{...colorProps}
|
||||
{...eventHandlers}
|
||||
accessibilityState={_accessibilityState}
|
||||
accessibilityLabel={_accessibilityLabel}
|
||||
accessibilityLabelledBy={_accessibilityLabelledBy}
|
||||
accessibilityState={_accessibilityState}
|
||||
accessible={accessible}
|
||||
acceptDragAndDropTypes={props.experimental_acceptDragAndDropTypes}
|
||||
autoCapitalize={autoCapitalize}
|
||||
|
||||
+1
@@ -432,6 +432,7 @@ jest.unmock('../TextInput');
|
||||
|
||||
expect(instance.toJSON()).toMatchInlineSnapshot(`
|
||||
<RCTSinglelineTextInputView
|
||||
accessibilityLabel="label"
|
||||
accessibilityState={
|
||||
Object {
|
||||
"busy": true,
|
||||
|
||||
+22
@@ -96,6 +96,8 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
|
||||
|
||||
@interface RCTModalHostViewComponentView () <RCTFabricModalHostViewControllerDelegate>
|
||||
|
||||
@property (nonatomic, weak) UIView *accessibilityFocusedView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTModalHostViewComponentView {
|
||||
@@ -148,6 +150,7 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
|
||||
{
|
||||
BOOL shouldBePresented = !_isPresented && _shouldPresent && self.window;
|
||||
if (shouldBePresented) {
|
||||
[self saveAccessibilityFocusedView];
|
||||
self.viewController.presentationController.delegate = self;
|
||||
|
||||
_isPresented = YES;
|
||||
@@ -179,6 +182,8 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
|
||||
if (eventEmitter) {
|
||||
eventEmitter->onDismiss(ModalHostViewEventEmitter::OnDismiss{});
|
||||
}
|
||||
|
||||
[self restoreAccessibilityFocusedView];
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -207,6 +212,23 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
|
||||
[self ensurePresentedOnlyIfNeeded];
|
||||
}
|
||||
|
||||
- (void)saveAccessibilityFocusedView
|
||||
{
|
||||
id focusedElement = UIAccessibilityFocusedElement(nil);
|
||||
if (focusedElement && [focusedElement isKindOfClass:[UIView class]]) {
|
||||
self.accessibilityFocusedView = (UIView *)focusedElement;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)restoreAccessibilityFocusedView
|
||||
{
|
||||
id viewToFocus = self.accessibilityFocusedView;
|
||||
if (viewToFocus) {
|
||||
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, viewToFocus);
|
||||
self.accessibilityFocusedView = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - RCTFabricModalHostViewControllerDelegate
|
||||
|
||||
- (void)boundsDidChange:(CGRect)newBounds
|
||||
|
||||
@@ -451,6 +451,19 @@ const examples: Array<RNTesterModuleExample> = [
|
||||
return <ToggleDefaultPaddingExample />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Accessibility',
|
||||
render: function (): React.Node {
|
||||
return (
|
||||
<View>
|
||||
<Text>accessibilityLabel prop</Text>
|
||||
<ExampleTextInput accessibilityLabel="This is Accessibility Label" />
|
||||
<Text>aria-label prop</Text>
|
||||
<ExampleTextInput aria-label="This is Aria Label" />
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = ({
|
||||
|
||||
@@ -1036,6 +1036,21 @@ const textInputExamples: Array<RNTesterModuleExample> = [
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Accessibility',
|
||||
render: function (): React.Node {
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="accessibilityLabel">
|
||||
<ExampleTextInput accessibilityLabel="This is Accessibility Label" />
|
||||
</WithLabel>
|
||||
<WithLabel label="aria-label">
|
||||
<ExampleTextInput aria-label="This is Aria Label" />
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = ({
|
||||
|
||||
Reference in New Issue
Block a user