Commit 100f8fc
Summary:
Replaces the two independent writers on `accessibilityTraits` (one in C++ for role, one in ObjC for state) with a single `deriveAccessibilityTraits()` helper that rebuilds the full mask from `None` each commit.
On recycled Fabric views, the ObjC writer's diff guard can skip when the stale `_props` match the new props, leaving `Selected` and `NotEnabled` traits stale.
Fixes #57515
NOTE: this is a fix, not a breaking change (see below)
0.88 is a non-breaking release, and two behaviours change here, so this was worth settling explicitly rather than by omission. Both stop the code doing something it should never have done, so the window does not gate this diff.
The first is `Selected` and `NotEnabled` surviving a view recycle, which is issue #57515 itself.
The second is that `role` no longer honours values outside the `Role` type. The old code parsed the raw `role` string into traits without validating it, so `role="image"` set the Image trait. The public `Role` type in `Libraries/Components/View/ViewAccessibility.js` has 65 members, matching the 65 `toString(Role)` outputs one for one, and none of the 17 affected trait names appear in it. `role="image"` was always a Flow and TypeScript error. Nine of the 17 are valid `accessibilityRole` values and keep working through that prop and the new fallback branch; the remaining eight are internal trait names that appear in neither public type. The full breakdown is in the test plan.
Residual risk worth naming: untyped JS that passes a runtime-computed string into `role` and happens to hit one of those nine would silently lose a trait. Typed code cannot reach this, and untyped code was relying on undocumented behaviour, but the determination rests on the declared types rather than on field data.
## Changelog:
[IOS] [FIXED] - Derive `accessibilityTraits` from role and state in a single writer so `Selected` and `NotEnabled` traits are not lost on recycled Fabric views
Pull Request resolved: #57516
Test Plan:
Reproducer repo: https://github.com/cgoldsby/rn-accessibility-traits-bug ([[steps](https://github.com/cgoldsby/rn-accessibility-traits-bug#steps-to-reproduce-bug)](https://github.com/cgoldsby/rn-accessibility-traits-bug#steps-to-reproduce-bug))
```sh
git clone https://github.com/cgoldsby/rn-accessibility-traits-bug.git
cd rn-accessibility-traits-bug
yarn install
cd ios && pod install && cd ..
yarn ios
```
1. Open Accessibility Inspector
2. Focus the target View
3. Tap "Toggle Role" to switch from `role="button"` to no role
**Before fix:** `Selected` trait is missing
**After fix:**
```sh
git apply patches/react-native+0.86.0.patch
cd ios && pod install && cd ..
yarn ios
```
`Selected` trait persists through the role change.
https://github.com/user-attachments/assets/79974b9d-59da-4066-9b31-e2feff91c407
## Unit tests
Adds `ReactCommon/react/renderer/components/view/tests/AccessibilityPropsTest.cpp`, picked up by the existing `:tests` glob. `AccessibilityProps` is a mixin rather than a `Props` descendant, so the tests parse through `ViewProps`, the concrete type that inherits it.
```
buck2 test fbsource//xplat/js/react-native-github/packages/react-native/ReactCommon/react/renderer/components/view:tests
→ Pass 87. Fail 0.
```
Three of the eight are true regression tests. Restoring the pre-fix `AccessibilityProps.cpp` and re-running fails exactly those three, with the other 84 still green:
```
✗ AccessibilityPropsTest.folds_selected_state_into_role_traits
✗ AccessibilityPropsTest.folds_disabled_state_into_role_traits
✗ AccessibilityPropsTest.clearing_selected_state_drops_the_trait
Tests finished: Pass 84. Fail 3.
```
The remaining five pin behaviour the refactor is *not* meant to change, and they pass against both the old and new code: traits derived from `role`, traits derived from `accessibilityRole`, `role` winning over `accessibilityRole`, no state traits when state is absent, and inheritance from `sourceProps` when the raw props are absent.
## NOTE: `role` no longer honours non-`Role` values (a fix, not a break)
The old code parsed the raw `role` value straight into traits. The new code only consults `role` once it has parsed to a valid `Role` enum, and otherwise falls back to `accessibilityRole`. Comparing the two string tables in `accessibilityPropsConversions.h`:
- `toString(Role)` can emit 65 strings; `fromString(-> AccessibilityTraits)` recognises 25.
- Only 8 overlap, so only these ever produced a trait, before or after: `button`, `heading`, `img`, `link`, `none`, `progressbar`, `summary`, `switch`.
- 17 trait names have no corresponding `Role`: `adjustable`, `allowsDirectInteraction`, `disabled`, `frequentUpdates`, `header`, `image`, `imagebutton`, `key`, `keyboardkey`, `pageTurn`, `plays`, `search`, `selected`, `startsMedia`, `tabbar`, `text`, `togglebutton`. Passing one of those through `role` used to set a trait and now yields `None`.
This is not a breaking change, because none of those were ever valid `role` values. The public `Role` type in `Libraries/Components/View/ViewAccessibility.js` has exactly 65 members, matching the 65 `toString(Role)` outputs one for one, and **none** of the 17 appear in it. `role="image"` was always a Flow and TypeScript type error; the old C++ honoured it only because it parsed the raw string into traits without validating it against the `Role` enum.
Of the 17, nine (`adjustable`, `header`, `image`, `imagebutton`, `keyboardkey`, `search`, `tabbar`, `text`, `togglebutton`) are valid `accessibilityRole` values and keep working through that prop and the fallback branch. The remaining eight are internal trait names that appear in neither public type.
The new code is also better in the mixed case: with an invalid `role` plus a valid `accessibilityRole`, the old code let the bad `role` win, whereas the new code falls back correctly.
## Also restored
The `// It is a (severe!) perf deoptimization to request props out-of-order` comment above the two `rawProps.at()` lookups. Its original rationale (needing `accessibilityRole` twice) is obsolete, but the general hazard is not, and the two lookups still have to stay adjacent and in order.
Reviewed By: javache
Differential Revision: D114880897
Pulled By: fabriziocucci
fbshipit-source-id: bfefa0db0d0c38895ad43d781afa99e9b0283ee7
1 parent 5fb3ebc commit 100f8fc
3 files changed
Lines changed: 186 additions & 54 deletions
File tree
- packages/react-native
- ReactCommon/react/renderer/components/view
- tests
- React/Fabric/Mounting/ComponentViews/View
Lines changed: 0 additions & 12 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
539 | 539 | | |
540 | 540 | | |
541 | 541 | | |
542 | | - | |
543 | | - | |
544 | | - | |
545 | | - | |
546 | | - | |
547 | | - | |
548 | | - | |
549 | | - | |
550 | | - | |
551 | | - | |
552 | | - | |
553 | | - | |
554 | 542 | | |
555 | 543 | | |
556 | 544 | | |
| |||
Lines changed: 65 additions & 42 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
17 | 42 | | |
18 | 43 | | |
19 | 44 | | |
| |||
146 | 171 | | |
147 | 172 | | |
148 | 173 | | |
149 | | - | |
150 | | - | |
151 | | - | |
152 | | - | |
153 | | - | |
154 | | - | |
155 | | - | |
| 174 | + | |
| 175 | + | |
156 | 176 | | |
157 | 177 | | |
158 | 178 | | |
159 | | - | |
160 | | - | |
161 | | - | |
162 | 179 | | |
163 | 180 | | |
164 | 181 | | |
| |||
172 | 189 | | |
173 | 190 | | |
174 | 191 | | |
175 | | - | |
176 | | - | |
177 | | - | |
178 | | - | |
179 | | - | |
| 192 | + | |
| 193 | + | |
180 | 194 | | |
181 | 195 | | |
182 | 196 | | |
| |||
188 | 202 | | |
189 | 203 | | |
190 | 204 | | |
191 | | - | |
192 | | - | |
193 | | - | |
194 | | - | |
195 | | - | |
196 | | - | |
197 | | - | |
198 | | - | |
199 | | - | |
200 | | - | |
201 | | - | |
202 | | - | |
203 | | - | |
204 | | - | |
205 | | - | |
206 | | - | |
207 | | - | |
208 | | - | |
209 | | - | |
210 | | - | |
211 | | - | |
212 | | - | |
213 | | - | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
214 | 239 | | |
215 | | - | |
216 | 240 | | |
217 | 241 | | |
218 | | - | |
219 | 242 | | |
220 | 243 | | |
221 | | - | |
222 | | - | |
223 | 244 | | |
| 245 | + | |
| 246 | + | |
224 | 247 | | |
225 | 248 | | |
226 | 249 | | |
| |||
Lines changed: 121 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
0 commit comments