Skip to content

Commit 9a8dca2

Browse files
javachemeta-codesync[bot]
authored andcommitted
Delegate TouchableNativeFeedback accessibility to its child (#59391)
Summary: Pull Request resolved: #59391 Forward unconsumed view props to the native-feedback child and let `View` normalise accessibility aliases. Preserve child styling and retain legacy alias resolution for non-View children without synthesising empty state or value props. Changelog: [General][Fixed] - Forward accessibility props consistently in TouchableNativeFeedback while preserving child styles. Differential Revision: D126907218
1 parent 28badcf commit 9a8dca2

2 files changed

Lines changed: 307 additions & 73 deletions

File tree

‎packages/react-native/Libraries/Components/Touchable/TouchableNativeFeedback.js‎

Lines changed: 98 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -275,90 +275,115 @@ class TouchableNativeFeedback extends React.Component<
275275
const {onBlur, onFocus, ...eventHandlersWithoutBlurAndFocus} =
276276
this.state.pressability.getEventHandlers();
277277

278-
const accessibilityStateProps: {
279-
accessibilityState?: TouchableNativeFeedbackProps['accessibilityState'],
280-
} = {};
281-
if (
282-
this.props.accessibilityState != null ||
283-
this.props['aria-busy'] != null ||
284-
this.props['aria-checked'] != null ||
285-
this.props['aria-disabled'] != null ||
286-
this.props['aria-expanded'] != null ||
287-
this.props['aria-selected'] != null ||
288-
this.props.disabled != null
289-
) {
290-
accessibilityStateProps.accessibilityState = {
291-
busy: this.props['aria-busy'] ?? this.props.accessibilityState?.busy,
292-
checked:
293-
this.props['aria-checked'] ?? this.props.accessibilityState?.checked,
294-
disabled:
295-
this.props.disabled ??
296-
this.props['aria-disabled'] ??
297-
this.props.accessibilityState?.disabled,
298-
expanded:
299-
this.props['aria-expanded'] ??
300-
this.props.accessibilityState?.expanded,
301-
selected:
302-
this.props['aria-selected'] ??
303-
this.props.accessibilityState?.selected,
304-
};
278+
const {
279+
accessible,
280+
background,
281+
children: childrenProp,
282+
delayLongPress,
283+
delayPressIn,
284+
delayPressOut,
285+
disabled,
286+
focusable,
287+
onBlur: _onBlur,
288+
onFocus: _onFocus,
289+
onLongPress,
290+
onPress,
291+
onPressIn,
292+
onPressOut,
293+
pressRetentionOffset,
294+
rejectResponderTermination,
295+
style: _style,
296+
touchSoundDisabled,
297+
useForeground,
298+
...restProps
299+
} = this.props;
300+
301+
const pressabilityConfig = this._createPressabilityConfig();
302+
if (pressabilityConfig.disabled != null) {
303+
// Expose Pressability's resolved disabled state to accessibility through View.
304+
restProps['aria-disabled'] = pressabilityConfig.disabled;
305305
}
306306

307-
const accessibilityValue = {
308-
max: this.props['aria-valuemax'] ?? this.props.accessibilityValue?.max,
309-
min: this.props['aria-valuemin'] ?? this.props.accessibilityValue?.min,
310-
now: this.props['aria-valuenow'] ?? this.props.accessibilityValue?.now,
311-
text: this.props['aria-valuetext'] ?? this.props.accessibilityValue?.text,
312-
};
307+
if (element.type !== View) {
308+
const accessibilityLabel =
309+
this.props['aria-label'] ?? this.props.accessibilityLabel;
310+
if (accessibilityLabel != null) {
311+
restProps.accessibilityLabel = accessibilityLabel;
312+
}
313+
const nativeID = this.props.id ?? this.props.nativeID;
314+
if (nativeID != null) {
315+
restProps.nativeID = nativeID;
316+
}
317+
const ariaHidden = this.props['aria-hidden'];
318+
if (ariaHidden != null) {
319+
restProps.accessibilityElementsHidden = ariaHidden;
320+
if (ariaHidden === true) {
321+
restProps.importantForAccessibility = 'no-hide-descendants';
322+
}
323+
}
313324

314-
const accessibilityLiveRegion =
315-
this.props['aria-live'] === 'off'
316-
? 'none'
317-
: (this.props['aria-live'] ?? this.props.accessibilityLiveRegion);
325+
const state = this.props.accessibilityState;
326+
const busy = this.props['aria-busy'] ?? state?.busy;
327+
const checked = this.props['aria-checked'] ?? state?.checked;
328+
const disabledState = restProps['aria-disabled'] ?? state?.disabled;
329+
const expanded = this.props['aria-expanded'] ?? state?.expanded;
330+
const selected = this.props['aria-selected'] ?? state?.selected;
331+
if (
332+
busy != null ||
333+
checked != null ||
334+
disabledState != null ||
335+
expanded != null ||
336+
selected != null
337+
) {
338+
restProps.accessibilityState = {
339+
busy,
340+
checked,
341+
disabled: disabledState,
342+
expanded,
343+
selected,
344+
};
345+
}
346+
347+
const ariaLive = this.props['aria-live'];
348+
if (ariaLive !== undefined) {
349+
restProps.accessibilityLiveRegion =
350+
ariaLive === 'off' ? 'none' : ariaLive;
351+
}
352+
if (this.props['aria-modal'] !== undefined) {
353+
restProps.accessibilityViewIsModal = this.props['aria-modal'];
354+
}
355+
if (
356+
this.props['aria-valuemax'] != null ||
357+
this.props['aria-valuemin'] != null ||
358+
this.props['aria-valuenow'] != null ||
359+
this.props['aria-valuetext'] != null
360+
) {
361+
restProps.accessibilityValue = {
362+
max:
363+
this.props['aria-valuemax'] ?? this.props.accessibilityValue?.max,
364+
min:
365+
this.props['aria-valuemin'] ?? this.props.accessibilityValue?.min,
366+
now:
367+
this.props['aria-valuenow'] ?? this.props.accessibilityValue?.now,
368+
text:
369+
this.props['aria-valuetext'] ?? this.props.accessibilityValue?.text,
370+
};
371+
}
372+
}
318373

319-
const accessibilityLabel =
320-
this.props['aria-label'] ?? this.props.accessibilityLabel;
321374
return cloneElement(
322375
element,
323376
{
377+
...restProps,
324378
...eventHandlersWithoutBlurAndFocus,
325379
...getBackgroundProp(
326-
this.props.background === undefined
380+
background === undefined
327381
? TouchableNativeFeedback.SelectableBackground()
328-
: this.props.background,
329-
this.props.useForeground === true,
382+
: background,
383+
useForeground === true,
330384
),
331-
accessible: this.props.accessible !== false,
332-
accessibilityHint: this.props.accessibilityHint,
333-
accessibilityLanguage: this.props.accessibilityLanguage,
334-
accessibilityLabel: accessibilityLabel,
335-
accessibilityRole: this.props.accessibilityRole,
336-
...accessibilityStateProps,
337-
accessibilityActions: this.props.accessibilityActions,
338-
onAccessibilityAction: this.props.onAccessibilityAction,
339-
accessibilityValue: accessibilityValue,
340-
importantForAccessibility:
341-
this.props['aria-hidden'] === true
342-
? 'no-hide-descendants'
343-
: this.props.importantForAccessibility,
344-
accessibilityViewIsModal:
345-
this.props['aria-modal'] ?? this.props.accessibilityViewIsModal,
346-
accessibilityLiveRegion: accessibilityLiveRegion,
347-
accessibilityElementsHidden:
348-
this.props['aria-hidden'] ?? this.props.accessibilityElementsHidden,
349-
hitSlop: this.props.hitSlop,
350-
focusable:
351-
this.props.focusable !== false &&
352-
this.props.onPress !== undefined &&
353-
!this.props.disabled,
354-
nativeID: this.props.id ?? this.props.nativeID,
355-
nextFocusDown: this.props.nextFocusDown,
356-
nextFocusForward: this.props.nextFocusForward,
357-
nextFocusLeft: this.props.nextFocusLeft,
358-
nextFocusRight: this.props.nextFocusRight,
359-
nextFocusUp: this.props.nextFocusUp,
360-
onLayout: this.props.onLayout,
361-
testID: this.props.testID,
385+
accessible: accessible !== false,
386+
focusable: focusable !== false && onPress !== undefined && !disabled,
362387
},
363388
...children,
364389
);

0 commit comments

Comments
 (0)