Skip to content

Report a disabled Switch as disabled to iOS accessibility - #59333

Open
arun279 wants to merge 2 commits into
react:mainfrom
arun279:fix-ios-switch-disabled-accessibility
Open

arun279 wants to merge 2 commits into
react:mainfrom
arun279:fix-ios-switch-disabled-accessibility

Conversation

@arun279

@arun279 arun279 commented Oct 22, 2026

Copy link
Copy Markdown

Summary:

On iOS, <Switch disabled /> is drawn greyed out and ignores taps, but VoiceOver and XCUITest still report it as enabled: the UIAccessibilityTraitNotEnabled trait is missing.

Since #50796, the inner UISwitch is the Switch's accessibility element, so RCTViewComponentView writes the traits it derives from the role onto the UISwitch, replacing the ones UIKit sets, including NotEnabled. Since #57516, accessibilityState.disabled adds NotEnabled to those traits, but the iOS branch of Switch.js never sets it from disabled. The Android branch already does.

This moves the existing Android logic above the platform check so iOS gets the same accessibilityState. As on Android, disabled wins over accessibilityState.disabled when both are set. It is done in Switch.js rather than RCTSwitchComponentView because the Android branch already does it here, and a Switch-specific native writer for the traits would bring back the second trait writer that #57516 removed.

disabled now behaves exactly like accessibilityState={{disabled: true}} already does. That includes a Switch whose role maps to no trait, such as accessibilityRole="none": it gets NotEnabled alone, without the switch trait.

Changelog:

[IOS] [FIXED] - Report a disabled Switch as disabled to VoiceOver and XCUITest

Test Plan:

Added Switch-test.js. Jest renders the iOS branch, while Switch-itest.js (Fantom) renders the Android one. Two of its three cases fail without this change.

yarn test packages/react-native/Libraries/Components/Switch
# 3 passed
yarn flow-check
# Found 0 errors
yarn eslint --max-warnings 0 packages/react-native/Libraries/Components/Switch/Switch.js packages/react-native/Libraries/Components/Switch/__tests__/Switch-test.js
# no warnings
yarn prettier --check packages/react-native/Libraries/Components/Switch/Switch.js packages/react-native/Libraries/Components/Switch/__tests__/Switch-test.js
# All matched files use Prettier code style!

Manual check: a new 0.88.0 app (prebuilt core; Switch.js is the same on 0.88.0 and main) on an iPhone 17 Pro simulator, iOS 27. For "After", this branch's Switch.js was copied into the app's node_modules/react-native/Libraries/Components/Switch/. The accessibility tree was read with AXe describe-ui, which reports each element's enabled attribute.

<Switch testID="disabled-mount" value disabled />
<Switch testID="disabled-live" value disabled={liveDisabled} />
<Switch testID="disabled-state" value disabled accessibilityState={{disabled: true}} />
Switch Before After
disabled-mount enabled not enabled
disabled-live, after disabled is set to true enabled not enabled
disabled-live, after disabled is set back to false enabled enabled
disabled-state not enabled not enabled

The role (switch) and value (1) are unchanged.

Not run locally: Switch-itest.js, which needs the Fantom native tester build. CI's Fantom job runs it, and the Android branch only had its existing logic moved. Also not run: VoiceOver on a device.

On iOS, the traits derived from the role and `accessibilityState` are written
onto the inner `UISwitch` and replace UIKit's own `NotEnabled` trait. The iOS
branch of `Switch.js` never folded `disabled` into `accessibilityState`, so a
disabled Switch was reported as enabled to VoiceOver and XCUITest. Share the
Android branch's existing logic with iOS.
@meta-cla

meta-cla Bot commented Oct 22, 2026

Copy link
Copy Markdown

Hi @arun279!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@meta-cla

meta-cla Bot commented Oct 22, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 22, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Oct 22, 2026
ref?: React.RefSetter<SwitchInstance>,
...SwitchProps,
}): React.Node {
const {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract accessibilityState here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 8180861, thanks.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants