Skip to content

Commit 9718f84

Browse files
authored
Implement accessibilityState (#4617)
* Implement AccessibilityState * formatting * Change files * bring back accessibilityStates to port back to 0.61 * formatting * handle mixed state * update test * prettier fixes
1 parent 4c320cf commit 9718f84

File tree

3 files changed

+101
-22
lines changed

3 files changed

+101
-22
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "implement accessibilityState",
4+
"packageName": "react-native-windows",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch",
7+
"date": "2020-04-15T22:25:43.418Z"
8+
}

vnext/ReactUWP/Views/FrameworkElementViewManager.cpp

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,12 +141,21 @@ void FrameworkElementViewManager::TransferProperties(XamlView oldView, XamlView
141141
}
142142
}
143143

144+
static folly::dynamic GetAccessibilityStateProps() {
145+
folly::dynamic props = folly::dynamic::object();
146+
147+
props.update(folly::dynamic::object("selected", "boolean")("disabled", "boolean")("checked", "string")(
148+
"busy", "boolean")("expanded", "boolean"));
149+
return props;
150+
}
151+
144152
folly::dynamic FrameworkElementViewManager::GetNativeProps() const {
145153
folly::dynamic props = Super::GetNativeProps();
146154
props.update(folly::dynamic::object("accessible", "boolean")("accessibilityRole", "string")(
147-
"accessibilityStates", "array")("accessibilityHint", "string")("accessibilityLabel", "string")(
148-
"accessibilityPosInSet", "number")("accessibilitySetSize", "number")("testID", "string")("tooltip", "string")(
149-
"accessibilityActions", "array")("accessibilityLiveRegion", "string"));
155+
"accessibilityStates", "array")("accessibilityState", GetAccessibilityStateProps())(
156+
"accessibilityHint", "string")("accessibilityLabel", "string")("accessibilityPosInSet", "number")(
157+
"accessibilitySetSize", "number")("testID", "string")("tooltip", "string")("accessibilityActions", "array")(
158+
"accessibilityLiveRegion", "string"));
150159
return props;
151160
}
152161

@@ -410,6 +419,49 @@ bool FrameworkElementViewManager::UpdateProperty(
410419
}
411420
}
412421

422+
DynamicAutomationProperties::SetAccessibilityStateSelected(
423+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Selected)]);
424+
DynamicAutomationProperties::SetAccessibilityStateDisabled(
425+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Disabled)]);
426+
DynamicAutomationProperties::SetAccessibilityStateChecked(
427+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Checked)]);
428+
DynamicAutomationProperties::SetAccessibilityStateUnchecked(
429+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Unchecked)]);
430+
DynamicAutomationProperties::SetAccessibilityStateBusy(
431+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Busy)]);
432+
DynamicAutomationProperties::SetAccessibilityStateExpanded(
433+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Expanded)]);
434+
DynamicAutomationProperties::SetAccessibilityStateCollapsed(
435+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Collapsed)]);
436+
} else if (propertyName == "accessibilityState") {
437+
bool states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::CountStates)] = {};
438+
439+
if (propertyValue.isObject()) {
440+
for (const auto &pair : propertyValue.items()) {
441+
const std::string &innerName = pair.first.getString();
442+
const folly::dynamic &innerValue = pair.second;
443+
444+
if (innerName == "selected")
445+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Selected)] = innerValue.getBool();
446+
else if (innerName == "disabled")
447+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Disabled)] = innerValue.getBool();
448+
else if (innerName == "checked") {
449+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Checked)] =
450+
innerValue.isBool() && innerValue.getBool();
451+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Unchecked)] =
452+
innerValue.isBool() && !innerValue.getBool();
453+
// If the state is "mixed" we'll just set both Checked and Unchecked to false,
454+
// then later in the IToggleProvider implementation it will return the Intermediate state
455+
// due to both being set to false (see DynamicAutomationPeer::ToggleState()).
456+
} else if (innerName == "busy")
457+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Busy)] = innerValue.getBool();
458+
else if (innerName == "expanded") {
459+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Expanded)] = innerValue.getBool();
460+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Collapsed)] = !innerValue.getBool();
461+
}
462+
}
463+
}
464+
413465
DynamicAutomationProperties::SetAccessibilityStateSelected(
414466
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Selected)]);
415467
DynamicAutomationProperties::SetAccessibilityStateDisabled(

vnext/src/RNTester/js/examples-win/Accessibility/AccessibilityExampleWindows.tsx

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ class AccessibilityStateExamples extends React.Component {
270270
public state = {
271271
viewDisabled: false,
272272
itemsSelected: [false, false, false],
273-
viewChecked: false,
273+
viewChecked: 0,
274274
viewBusy: false,
275275
viewCollapsed: false,
276276
};
@@ -294,7 +294,7 @@ class AccessibilityStateExamples extends React.Component {
294294
backgroundColor: this.state.viewDisabled ? 'gray' : 'lightskyblue',
295295
}}
296296
accessibilityRole="none"
297-
accessibilityStates={this.state.viewDisabled ? ['disabled'] : []}>
297+
accessibilityState={{disabled: this.state.viewDisabled}}>
298298
<Text>
299299
This View should be{' '}
300300
{this.state.viewDisabled ? 'disabled' : 'enabled'} according to UIA
@@ -318,9 +318,9 @@ class AccessibilityStateExamples extends React.Component {
318318
}}
319319
accessibilityRole="button"
320320
accessibilityLabel={'Selectable item ' + (item.index + 1)}
321-
accessibilityStates={
322-
this.state.itemsSelected[item.index] ? ['selected'] : []
323-
}
321+
accessibilityState={{
322+
selected: this.state.itemsSelected[item.index],
323+
}}
324324
onPress={() => this.selectPress(item.index)}>
325325
<Text>
326326
{this.state.itemsSelected[item.index]
@@ -332,8 +332,8 @@ class AccessibilityStateExamples extends React.Component {
332332
keyExtractor={(item, index) => index.toString()}
333333
/>
334334
<Text>
335-
The following TouchableHighlight toggles accessibilityState.checked
336-
and accessibilityState.unchecked for the View under it:
335+
The following TouchableHighlight cycles accessibilityState.checked
336+
through unchecked/checked/mixed for the View under it:
337337
</Text>
338338
<TouchableHighlight
339339
style={{width: 100, height: 50, backgroundColor: 'blue'}}
@@ -343,22 +343,37 @@ class AccessibilityStateExamples extends React.Component {
343343
</TouchableHighlight>
344344
<View
345345
style={{
346-
backgroundColor: this.state.viewChecked ? 'gray' : 'lightskyblue',
346+
backgroundColor:
347+
this.state.viewChecked === 0
348+
? 'green'
349+
: this.state.viewChecked === 1
350+
? 'gray'
351+
: 'lightskyblue',
347352
}}
348353
//@ts-ignore
349354
accessibilityRole="checkbox"
350355
//@ts-ignore
351-
accessibilityStates={
352-
this.state.viewChecked ? ['checked'] : ['unchecked']
353-
}>
356+
accessibilityState={{
357+
checked:
358+
this.state.viewChecked === 0
359+
? false
360+
: this.state.viewChecked === 1
361+
? true
362+
: 'mixed',
363+
}}>
354364
<Text>
355365
This View should be{' '}
356-
{this.state.viewChecked ? 'Checked' : 'Unchecked'} according to UIA
366+
{this.state.viewChecked === 0
367+
? 'Unchecked'
368+
: this.state.viewChecked === 1
369+
? 'Checked'
370+
: 'Mixed'}{' '}
371+
according to UIA
357372
</Text>
358373
</View>
359374
<Text>
360-
The following TouchableHighlight toggles accessibilityState.busy for
361-
the View under it:
375+
The following TouchableHighlight toggles the acessibilityState.busy
376+
for the View under it:
362377
</Text>
363378
<TouchableHighlight
364379
style={{width: 100, height: 50, backgroundColor: 'blue'}}
@@ -372,7 +387,7 @@ class AccessibilityStateExamples extends React.Component {
372387
}}
373388
accessibilityRole="none"
374389
//@ts-ignore
375-
accessibilityStates={this.state.viewBusy ? ['busy'] : []}>
390+
accessibilityState={{busy: this.state.viewBusy}}>
376391
<Text>
377392
This View should be {this.state.viewBusy ? 'Busy' : 'Not Busy'}{' '}
378393
according to UIA
@@ -395,9 +410,9 @@ class AccessibilityStateExamples extends React.Component {
395410
}}
396411
accessibilityRole="none"
397412
//@ts-ignore
398-
accessibilityStates={
399-
this.state.viewCollapsed ? ['collapsed'] : ['expanded']
400-
}>
413+
accessibilityState={{
414+
expanded: !this.state.viewCollapsed,
415+
}}>
401416
<Text>
402417
This View should be{' '}
403418
{this.state.viewCollapsed ? 'Collapsed' : 'Expanded'} according to
@@ -419,7 +434,11 @@ class AccessibilityStateExamples extends React.Component {
419434
};
420435

421436
private checkedPress = () => {
422-
this.setState({viewChecked: !this.state.viewChecked});
437+
let newChecked = this.state.viewChecked + 1;
438+
if (newChecked === 3) {
439+
newChecked = 0;
440+
}
441+
this.setState({viewChecked: newChecked});
423442
};
424443

425444
private busyPress = () => {

0 commit comments

Comments
 (0)