Repository navigation
fix(macos): Fabric accessibility roles and state, and TextInput read-only and Tab-stop fixes - #3136
Open
Jason Morse (JasonVMo) wants to merge 6 commits into
Open
Jason Morse (JasonVMo) wants to merge 6 commits into
Jason Morse (JasonVMo) wants to merge 6 commits into
Conversation
+[RCTConvert accessibilityRoleFromTraits:useAriaMappings:] passed the whole array, rather than the current element, to the ARIA lookup, so array input never matched an ARIA role. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ityRole On macOS, RCTViewComponentView mapped only accessibilityTraits to a role. The C++ props convert just a few role strings to traits, so roles such as menuitem, option, tab, combobox and textbox reported AXUnknown. The role was also set on the component view rather than its accessibility element, so the field behind a TextInput never received it. Resolve the role the way Paper does: role with the ARIA table, then accessibilityRole with the legacy table, then the traits, and otherwise the element's default role. Write it to accessibilityElement. Add the missing legacy textbox mapping and an RNTester example. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fabric ignored accessibilityState on macOS, so VoiceOver and automation reported disabled elements as enabled and never reported AXSelected or AXExpanded. Project the state, including the NotEnabled and Selected traits, onto the accessibility element. This replaces the trait-only accessibilityEnabled update, which wrote to the component view instead of the element. As in Paper, expose an attribute only when JS provides it: setting an NSAccessibility state makes AppKit advertise the attribute, so an element without expanded must not report AXExpanded. Add an RNTester example. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…State.disabled
editable={false} disabled the single-line NSTextField, so read-only fields
could not be focused, selected or copied and reported AXEnabled = NO, while
accessibilityState.disabled did not disable the field at all.
- RCTUITextField: editable now only controls editing. Read-only fields stay
enabled, selectable and reachable with Tab.
- accessibilityState.disabled sets a new enabled property on the backing
text input on both renderers. RCTUITextView implements it for multiline
input: it refuses first responder and reports AXEnabled = NO.
- Paper: clear the previous state when accessibilityState is removed.
Disabled fields now use AppKit's disabled appearance; read-only fields no
longer do. Add an RNTester example.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
RCTTextInputComponentView and, for multiline input, RCTWrappedTextView only
host the editor but were in the key view loop, so each TextInput took two
Tab presses. TextInput's default focusable={true} also let the component
view become first responder.
Keep both wrappers out of the key view loop and the responder chain; focus()
already targets the editor. RNTester now recalculates its key view loop like
RCTAppDelegate, so Tab moves between views in the new example.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… view Changing multiline or secureTextEntry, including on a recycled view, replaces the backing view and copies its accessibility role. An unset role reads back as AXUnknown, so the new NSTextField reported AXUnknown instead of AXTextField. Do not copy an unset role. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
7 tasks
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
On macOS, Fabric loses most accessibility information that Paper exposes, and TextInput handles focus and read-only state incorrectly:
role/accessibilityRolevalues such asmenuitem,option,tab,comboboxandtextboxreportAXUnknown.accessibilityStatedisabled,selectedandexpandednever reach AppKit.editable={false}is disabled instead of read-only. It can't be focused or selected, whileaccessibilityState.disableddoesn't disable it at all.FluentUI React Native works around these with a Yarn patch on 0.81.9 (microsoft/fluentui-react-native#4386). This PR fixes them on
main. All changes are macOS-only and tagged. The 0.81 backport and the Paper checks that can't run onmainare tracked in #3135.Each commit is a separate fix and builds on its own:
RCTConvert: the ARIA path for array input passed the whole array instead of each element.rolewith the ARIA table, thenaccessibilityRolewith the legacy table, then the traits, then the element's default. Write it toaccessibilityElement, so TextInput's backing field receives it. Also add the missing legacytextboxmapping.disabled,selectedandexpanded, including the NotEnabled and Selected traits, onto the accessibility element. As in Paper,isAccessibilitySelectorAllowed:exposes an attribute only when JS provides it, so a view withoutexpandeddoesn't report AXExpanded.editableonly controls editing; read-only fields stay enabled, selectable and reachable with Tab.accessibilityState.disableddrives a newenabledproperty on the backing input on both renderers.RCTUITextViewimplements it for multiline. On Paper, removing theaccessibilityStateprop now clears the previous state.RCTWrappedTextViewstay out of the key-view loop. The component view also refuses first responder, since TextInput defaultsfocusableto true. RNTester-macOS now recalculates its key-view loop, matchingRCTAppDelegate.AXUnknown. The new field then reportedAXUnknowninstead ofAXTextField.New RNTester examples cover each fix: Accessibility "Roles from role and accessibilityRole" and "Disabled, selected and expanded state"; TextInput "Read-only and disabled" and "Tab stops".
Follow-ups outside this PR:
roleoraccessibilityRolekeeps the previous value, because sharedAccessibilityPropsparsing treatsnullas unset.role="none"restores the default role.accessibilityValueoverride still appends "expanded", which may make VoiceOver say it twice now that AXExpanded is exposed.checkedandbusyaren't projected, and Fabric views expose no AXPress action.Test Plan:
RCT_NEW_ARCH_ENABLED=1 pod install(inpackages/rn-tester)xcodebuild -workspace packages/rn-tester/RNTesterPods.xcworkspace -sdk macosx -configuration Debug -scheme RNTester-macOS CODE_SIGNING_ALLOWED=NO buildxcodebuild -workspace packages/rn-tester/RNTesterPods.xcworkspace -sdk iphonesimulator -configuration Debug -scheme RNTester -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO buildyarn flow checkyarn exec eslint --max-warnings 0andyarn exec prettier --checkon the two example filesI ran the new examples in RNTester-macOS (new architecture) and inspected them from a separate process with
AXUIElementCopyAttributeValue. Both runs used the RNTester key-view change, so Tab can move between views.mainrole="menuitem"/"option"/"tab"AXUnknownAXMenuItem/AXStaticText/AXRadioButtonaccessibilityRole="textbox"AXTextField"none"AXUnknown"none"restoresAXUnknownrole="combobox", then"none"AXUnknownAXComboBox, thenAXTextFielddisabled,selected,expandedon and offexpandededitable={false}accessibilityState={{disabled: true}}, single-line and multilineAXUnknownAXTextFieldNot run:
maindoesn't build the legacy architecture; tracked in Backport macOS accessibility and TextInput fixes to 0.81-stable, with Paper validation #3135.RNTester-macOSUnitTestsalready fails to compile onmain(RCTImageLoaderTests.musesUIImage).