Skip to content

Commit b08b497

Browse files
committed
Implement accessibilityState (microsoft#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 ec13507 commit b08b497

File tree

3 files changed

+102
-23
lines changed

3 files changed

+102
-23
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: 56 additions & 4 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

@@ -156,7 +165,7 @@ void FrameworkElementViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate,
156165
for (const auto &pair : reactDiffMap.items()) {
157166
const std::string &propertyName = pair.first.getString();
158167
const folly::dynamic &propertyValue = pair.second;
159-
168+
160169
if (propertyName == "opacity") {
161170
if (propertyValue.isNumber()) {
162171
double opacity = propertyValue.asDouble();
@@ -436,6 +445,49 @@ void FrameworkElementViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate,
436445
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Expanded)]);
437446
DynamicAutomationProperties::SetAccessibilityStateCollapsed(
438447
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Collapsed)]);
448+
} else if (propertyName == "accessibilityState") {
449+
bool states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::CountStates)] = {};
450+
451+
if (propertyValue.isObject()) {
452+
for (const auto &pair : propertyValue.items()) {
453+
const std::string &innerName = pair.first.getString();
454+
const folly::dynamic &innerValue = pair.second;
455+
456+
if (innerName == "selected")
457+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Selected)] = innerValue.getBool();
458+
else if (innerName == "disabled")
459+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Disabled)] = innerValue.getBool();
460+
else if (innerName == "checked") {
461+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Checked)] =
462+
innerValue.isBool() && innerValue.getBool();
463+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Unchecked)] =
464+
innerValue.isBool() && !innerValue.getBool();
465+
// If the state is "mixed" we'll just set both Checked and Unchecked to false,
466+
// then later in the IToggleProvider implementation it will return the Intermediate state
467+
// due to both being set to false (see DynamicAutomationPeer::ToggleState()).
468+
} else if (innerName == "busy")
469+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Busy)] = innerValue.getBool();
470+
else if (innerName == "expanded") {
471+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Expanded)] = innerValue.getBool();
472+
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Collapsed)] = !innerValue.getBool();
473+
}
474+
}
475+
}
476+
477+
DynamicAutomationProperties::SetAccessibilityStateSelected(
478+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Selected)]);
479+
DynamicAutomationProperties::SetAccessibilityStateDisabled(
480+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Disabled)]);
481+
DynamicAutomationProperties::SetAccessibilityStateChecked(
482+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Checked)]);
483+
DynamicAutomationProperties::SetAccessibilityStateUnchecked(
484+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Unchecked)]);
485+
DynamicAutomationProperties::SetAccessibilityStateBusy(
486+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Busy)]);
487+
DynamicAutomationProperties::SetAccessibilityStateExpanded(
488+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Expanded)]);
489+
DynamicAutomationProperties::SetAccessibilityStateCollapsed(
490+
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Collapsed)]);
439491
} else if (propertyName == "testID") {
440492
if (propertyValue.isString()) {
441493
auto value = react::uwp::asHstring(propertyValue);

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

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ class AccessibilityStateExamples extends React.Component {
269269
public state = {
270270
viewDisabled: false,
271271
itemsSelected: [false, false, false],
272-
viewChecked: false,
272+
viewChecked: 0,
273273
viewBusy: false,
274274
viewCollapsed: false,
275275
};
@@ -293,7 +293,7 @@ class AccessibilityStateExamples extends React.Component {
293293
backgroundColor: this.state.viewDisabled ? 'gray' : 'lightskyblue',
294294
}}
295295
accessibilityRole="none"
296-
accessibilityStates={this.state.viewDisabled ? ['disabled'] : []}>
296+
accessibilityState={{disabled: this.state.viewDisabled}}>
297297
<Text>
298298
This View should be{' '}
299299
{this.state.viewDisabled ? 'disabled' : 'enabled'} according to UIA
@@ -317,9 +317,9 @@ class AccessibilityStateExamples extends React.Component {
317317
}}
318318
accessibilityRole="button"
319319
accessibilityLabel={'Selectable item ' + (item.index + 1)}
320-
accessibilityStates={
321-
this.state.itemsSelected[item.index] ? ['selected'] : []
322-
}
320+
accessibilityState={{
321+
selected: this.state.itemsSelected[item.index],
322+
}}
323323
onPress={() => this.selectPress(item.index)}>
324324
<Text>
325325
{this.state.itemsSelected[item.index]
@@ -331,8 +331,8 @@ class AccessibilityStateExamples extends React.Component {
331331
keyExtractor={(item, index) => index.toString()}
332332
/>
333333
<Text>
334-
The following TouchableHighlight toggles accessibilityState.checked
335-
and accessibilityState.unchecked for the View under it:
334+
The following TouchableHighlight cycles accessibilityState.checked
335+
through unchecked/checked/mixed for the View under it:
336336
</Text>
337337
<TouchableHighlight
338338
style={{width: 100, height: 50, backgroundColor: 'blue'}}
@@ -342,22 +342,37 @@ class AccessibilityStateExamples extends React.Component {
342342
</TouchableHighlight>
343343
<View
344344
style={{
345-
backgroundColor: this.state.viewChecked ? 'gray' : 'lightskyblue',
345+
backgroundColor:
346+
this.state.viewChecked === 0
347+
? 'green'
348+
: this.state.viewChecked === 1
349+
? 'gray'
350+
: 'lightskyblue',
346351
}}
347352
//@ts-ignore
348353
accessibilityRole="checkbox"
349354
//@ts-ignore
350-
accessibilityStates={
351-
this.state.viewChecked ? ['checked'] : ['unchecked']
352-
}>
355+
accessibilityState={{
356+
checked:
357+
this.state.viewChecked === 0
358+
? false
359+
: this.state.viewChecked === 1
360+
? true
361+
: 'mixed',
362+
}}>
353363
<Text>
354364
This View should be{' '}
355-
{this.state.viewChecked ? 'Checked' : 'Unchecked'} according to UIA
365+
{this.state.viewChecked === 0
366+
? 'Unchecked'
367+
: this.state.viewChecked === 1
368+
? 'Checked'
369+
: 'Mixed'}{' '}
370+
according to UIA
356371
</Text>
357372
</View>
358373
<Text>
359-
The following TouchableHighlight toggles accessibilityState.busy for
360-
the View under it:
374+
The following TouchableHighlight toggles the acessibilityState.busy
375+
for the View under it:
361376
</Text>
362377
<TouchableHighlight
363378
style={{width: 100, height: 50, backgroundColor: 'blue'}}
@@ -371,7 +386,7 @@ class AccessibilityStateExamples extends React.Component {
371386
}}
372387
accessibilityRole="none"
373388
//@ts-ignore
374-
accessibilityStates={this.state.viewBusy ? ['busy'] : []}>
389+
accessibilityState={{busy: this.state.viewBusy}}>
375390
<Text>
376391
This View should be {this.state.viewBusy ? 'Busy' : 'Not Busy'}{' '}
377392
according to UIA
@@ -394,9 +409,9 @@ class AccessibilityStateExamples extends React.Component {
394409
}}
395410
accessibilityRole="none"
396411
//@ts-ignore
397-
accessibilityStates={
398-
this.state.viewCollapsed ? ['collapsed'] : ['expanded']
399-
}>
412+
accessibilityState={{
413+
expanded: !this.state.viewCollapsed,
414+
}}>
400415
<Text>
401416
This View should be{' '}
402417
{this.state.viewCollapsed ? 'Collapsed' : 'Expanded'} according to
@@ -418,7 +433,11 @@ class AccessibilityStateExamples extends React.Component {
418433
};
419434

420435
private checkedPress = () => {
421-
this.setState({viewChecked: !this.state.viewChecked});
436+
let newChecked = this.state.viewChecked + 1;
437+
if (newChecked === 3) {
438+
newChecked = 0;
439+
}
440+
this.setState({viewChecked: newChecked});
422441
};
423442

424443
private busyPress = () => {

0 commit comments

Comments
 (0)