mirror of
https://github.com/react/react-native.git
synced 2026-09-28 13:23:09 +08:00
Summary: Pull Request resolved: https://github.com/react/react-native/pull/58599 `AccessibilityState::selected` was a plain `bool` defaulting to `false`, so the native side could not tell a component that is selectable but currently unselected (`accessibilityState={{selected: false}}`) from one that is not selectable at all (`accessibilityState={{}}`). Both arrived as `false`. The JS type is already `selected?: ?boolean`, so this is the bridge discarding a value the public API accepts. Make `selected` a `std::optional<bool>` defaulting to `std::nullopt`. JS accessibilityState native selected (before -> after) {} false -> undefined {selected: false} false -> false {selected: true} true -> true This aligns the representation with ARIA, which `accessibilityState` mirrors: the bool / tri-state split now tracks which ARIA attributes admit an undefined value. field ARIA value type admits undefined representation disabled boolean no bool busy boolean no bool selected boolean yes std::optional<bool> (changed) expanded boolean yes std::optional<bool> checked tristate yes CheckedState (None = unset) That is also why `disabled` and `busy` stay plain `bool`: ARIA gives them no undefined value, so there is no unset state to preserve. `expanded` was made optional for this same reason in https://github.com/facebook/react-native/pull/40881 and `checked` has always carried a `None`; `selected` was the outlier. Nor is "unset" merely "absent" for this attribute. `testing-library/dom` computes it as `boolean | undefined`, documented "false/true if (not)selected, undefined if not selectable" -- the same shape, with the same meaning, that this change introduces. Host platforms need the distinction: on Windows a selectable component must implement ISelectionItemProvider so UIA can report selection state, and with the old representation every component carrying an accessibilityState looked selectable. iOS and Android rendering is unchanged. Trait derivation coalesces the optional with `value_or(false)`, and the Android serializer omits the key when the value is unset, which `BaseViewManager#setViewState` already handles by falling back to `setSelected(false)`. Reviewer note: `std::optional<bool>` is contextually convertible to `bool`, so a bare `if (state.selected)` still compiles but tests engagement rather than value, silently marking an explicitly unselected component as selected. There is a regression test for that specific hazard. Fixes https://github.com/facebook/react-native/issues/46988 Supersedes https://github.com/facebook/react-native/pull/47296, which went stale. Changelog: [General][Breaking] - `AccessibilityState::selected` is now `std::optional<bool>` in C++ props, preserving an unset `selected` instead of coercing it to `false` Reviewed By: javache Differential Revision: D120049025 fbshipit-source-id: 53ac440976f00daffb3f0599c8841ada2a1452a6