Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement accessibilityState (#4617) #4624

Merged
5 commits merged into from
Apr 17, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "prerelease",
"comment": "implement accessibilityState",
"packageName": "react-native-windows",
"email": "kmelmon@microsoft.com",
"dependentChangeType": "patch",
"date": "2020-04-15T22:25:43.418Z"
}
60 changes: 56 additions & 4 deletions vnext/ReactUWP/Views/FrameworkElementViewManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,21 @@ void FrameworkElementViewManager::TransferProperties(XamlView oldView, XamlView
}
}

static folly::dynamic GetAccessibilityStateProps() {
folly::dynamic props = folly::dynamic::object();

props.update(folly::dynamic::object("selected", "boolean")("disabled", "boolean")("checked", "string")(
"busy", "boolean")("expanded", "boolean"));
return props;
}

folly::dynamic FrameworkElementViewManager::GetNativeProps() const {
folly::dynamic props = Super::GetNativeProps();
props.update(folly::dynamic::object("accessible", "boolean")("accessibilityRole", "string")(
"accessibilityStates", "array")("accessibilityHint", "string")("accessibilityLabel", "string")(
"accessibilityPosInSet", "number")("accessibilitySetSize", "number")("testID", "string")("tooltip", "string")(
"accessibilityActions", "array")("accessibilityLiveRegion", "string"));
"accessibilityStates", "array")("accessibilityState", GetAccessibilityStateProps())(
"accessibilityHint", "string")("accessibilityLabel", "string")("accessibilityPosInSet", "number")(
"accessibilitySetSize", "number")("testID", "string")("tooltip", "string")("accessibilityActions", "array")(
"accessibilityLiveRegion", "string"));
return props;
}

Expand All @@ -156,7 +165,7 @@ void FrameworkElementViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate,
for (const auto &pair : reactDiffMap.items()) {
const std::string &propertyName = pair.first.getString();
const folly::dynamic &propertyValue = pair.second;

if (propertyName == "opacity") {
if (propertyValue.isNumber()) {
double opacity = propertyValue.asDouble();
Expand Down Expand Up @@ -436,6 +445,49 @@ void FrameworkElementViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate,
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Expanded)]);
DynamicAutomationProperties::SetAccessibilityStateCollapsed(
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Collapsed)]);
} else if (propertyName == "accessibilityState") {
bool states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::CountStates)] = {};

if (propertyValue.isObject()) {
for (const auto &innerPair : propertyValue.items()) {
const std::string &innerName = innerPair.first.getString();
const folly::dynamic &innerValue = pair.second;

if (innerName == "selected")
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Selected)] = innerValue.getBool();
else if (innerName == "disabled")
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Disabled)] = innerValue.getBool();
else if (innerName == "checked") {
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Checked)] =
innerValue.isBool() && innerValue.getBool();
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Unchecked)] =
innerValue.isBool() && !innerValue.getBool();
// If the state is "mixed" we'll just set both Checked and Unchecked to false,
// then later in the IToggleProvider implementation it will return the Intermediate state
// due to both being set to false (see DynamicAutomationPeer::ToggleState()).
} else if (innerName == "busy")
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Busy)] = innerValue.getBool();
else if (innerName == "expanded") {
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Expanded)] = innerValue.getBool();
states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Collapsed)] = !innerValue.getBool();
}
}
}

DynamicAutomationProperties::SetAccessibilityStateSelected(
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Selected)]);
DynamicAutomationProperties::SetAccessibilityStateDisabled(
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Disabled)]);
DynamicAutomationProperties::SetAccessibilityStateChecked(
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Checked)]);
DynamicAutomationProperties::SetAccessibilityStateUnchecked(
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Unchecked)]);
DynamicAutomationProperties::SetAccessibilityStateBusy(
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Busy)]);
DynamicAutomationProperties::SetAccessibilityStateExpanded(
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Expanded)]);
DynamicAutomationProperties::SetAccessibilityStateCollapsed(
element, states[static_cast<int32_t>(winrt::react::uwp::AccessibilityStates::Collapsed)]);
} else if (propertyName == "testID") {
if (propertyValue.isString()) {
auto value = react::uwp::asHstring(propertyValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ class AccessibilityStateExamples extends React.Component {
public state = {
viewDisabled: false,
itemsSelected: [false, false, false],
viewChecked: false,
viewChecked: 0,
viewBusy: false,
viewCollapsed: false,
};
Expand All @@ -293,7 +293,7 @@ class AccessibilityStateExamples extends React.Component {
backgroundColor: this.state.viewDisabled ? 'gray' : 'lightskyblue',
}}
accessibilityRole="none"
accessibilityStates={this.state.viewDisabled ? ['disabled'] : []}>
accessibilityState={{disabled: this.state.viewDisabled}}>
<Text>
This View should be{' '}
{this.state.viewDisabled ? 'disabled' : 'enabled'} according to UIA
Expand All @@ -317,9 +317,9 @@ class AccessibilityStateExamples extends React.Component {
}}
accessibilityRole="button"
accessibilityLabel={'Selectable item ' + (item.index + 1)}
accessibilityStates={
this.state.itemsSelected[item.index] ? ['selected'] : []
}
accessibilityState={{
selected: this.state.itemsSelected[item.index],
}}
onPress={() => this.selectPress(item.index)}>
<Text>
{this.state.itemsSelected[item.index]
Expand All @@ -331,8 +331,8 @@ class AccessibilityStateExamples extends React.Component {
keyExtractor={(item, index) => index.toString()}
/>
<Text>
The following TouchableHighlight toggles accessibilityState.checked
and accessibilityState.unchecked for the View under it:
The following TouchableHighlight cycles accessibilityState.checked
through unchecked/checked/mixed for the View under it:
</Text>
<TouchableHighlight
style={{width: 100, height: 50, backgroundColor: 'blue'}}
Expand All @@ -342,22 +342,37 @@ class AccessibilityStateExamples extends React.Component {
</TouchableHighlight>
<View
style={{
backgroundColor: this.state.viewChecked ? 'gray' : 'lightskyblue',
backgroundColor:
this.state.viewChecked === 0
? 'green'
: this.state.viewChecked === 1
? 'gray'
: 'lightskyblue',
}}
//@ts-ignore
accessibilityRole="checkbox"
//@ts-ignore
accessibilityStates={
this.state.viewChecked ? ['checked'] : ['unchecked']
}>
accessibilityState={{
checked:
this.state.viewChecked === 0
? false
: this.state.viewChecked === 1
? true
: 'mixed',
}}>
<Text>
This View should be{' '}
{this.state.viewChecked ? 'Checked' : 'Unchecked'} according to UIA
{this.state.viewChecked === 0
? 'Unchecked'
: this.state.viewChecked === 1
? 'Checked'
: 'Mixed'}{' '}
according to UIA
</Text>
</View>
<Text>
The following TouchableHighlight toggles accessibilityState.busy for
the View under it:
The following TouchableHighlight toggles the acessibilityState.busy
for the View under it:
</Text>
<TouchableHighlight
style={{width: 100, height: 50, backgroundColor: 'blue'}}
Expand All @@ -371,7 +386,7 @@ class AccessibilityStateExamples extends React.Component {
}}
accessibilityRole="none"
//@ts-ignore
accessibilityStates={this.state.viewBusy ? ['busy'] : []}>
accessibilityState={{busy: this.state.viewBusy}}>
<Text>
This View should be {this.state.viewBusy ? 'Busy' : 'Not Busy'}{' '}
according to UIA
Expand All @@ -394,9 +409,9 @@ class AccessibilityStateExamples extends React.Component {
}}
accessibilityRole="none"
//@ts-ignore
accessibilityStates={
this.state.viewCollapsed ? ['collapsed'] : ['expanded']
}>
accessibilityState={{
expanded: !this.state.viewCollapsed,
}}>
<Text>
This View should be{' '}
{this.state.viewCollapsed ? 'Collapsed' : 'Expanded'} according to
Expand All @@ -418,7 +433,11 @@ class AccessibilityStateExamples extends React.Component {
};

private checkedPress = () => {
this.setState({viewChecked: !this.state.viewChecked});
let newChecked = this.state.viewChecked + 1;
if (newChecked === 3) {
newChecked = 0;
}
this.setState({viewChecked: newChecked});
};

private busyPress = () => {
Expand Down