Skip to content

Commit a2a9434

Browse files
vineethkuttanVineeth K
and
Vineeth K
authored
Implement accessibilityLevel for Fabric (#14593)
* Implement accessibilityLevel for Fabric * Change files * Added Test Cases for the accessibility Label, Level etc. * Updating snapshots --------- Co-authored-by: Vineeth K <kvineeth@microsoft.com>
1 parent 6e4a8f0 commit a2a9434

File tree

10 files changed

+157
-2
lines changed

10 files changed

+157
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "Implement accessibilityLevel for Fabric",
4+
"packageName": "react-native-windows",
5+
"email": "kvineeth@microsoft.com",
6+
"dependentChangeType": "patch"
7+
}

packages/@react-native-windows/tester/src/js/examples-win/Accessibility/AccessibilityExampleWindows.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,35 @@ class AccessibilityBaseExample extends React.Component {
2222
<Text>The following has accessibilityLabel and accessibilityHint:</Text>
2323
<View
2424
style={{width: 50, height: 50, backgroundColor: 'blue'}}
25+
accessible={true}
2526
accessibilityLabel="A blue box"
2627
accessibilityHint="A hint for the blue box."
28+
accessibilityLevel={1}
29+
testID="accessibility-base-view-1"
2730
/>
2831
<Text>The following has accessible and accessibilityLabel:</Text>
2932
<View
3033
style={{width: 50, height: 50, backgroundColor: 'red'}}
3134
accessible={true}
3235
accessibilityLabel="A hint for the red box."
36+
accessibilityLevel={2}
37+
testID="accessibility-base-view-2"
3338
/>
3439
<Text>
3540
The following has accessibilitySetSize, accessibilityPosInSet and
3641
accessibilityLabel:
3742
</Text>
3843
<View
3944
style={{width: 50, height: 50, backgroundColor: 'red'}}
45+
testID="accessibility-base-view-3"
4046
accessible={true}
4147
accessibilityRole="listitem"
4248
accessibilityLabel="This label should not be used"
4349
aria-label="Aria label takes precedence"
4450
accessibilitySetSize={5}
4551
accessibilityPosInSet={2}
46-
accessibilityLevel={4}
52+
aria-level={9} //aria-level takes precedence over accessibilityLevel
53+
accessibilityLevel={5}
4754
/>
4855
</View>
4956
);

packages/e2e-test-app-fabric/test/AccessibilityTest.test.ts

+27
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,31 @@ describe('Accessibility Tests', () => {
7777
const dump = await dumpVisualTree('accessibilityValue-text');
7878
expect(dump).toMatchSnapshot();
7979
});
80+
test('Accessibility data for Label,Level and Hint', async () => {
81+
await searchBox('Lab');
82+
const componentsTab = await app.findElementByTestID(
83+
'accessibility-base-view-1',
84+
);
85+
await componentsTab.waitForDisplayed({timeout: 5000});
86+
const dump = await dumpVisualTree('accessibility-base-view-1');
87+
expect(dump).toMatchSnapshot();
88+
});
89+
test('Accessibility data for Label and Level', async () => {
90+
await searchBox('Lab');
91+
const componentsTab = await app.findElementByTestID(
92+
'accessibility-base-view-2',
93+
);
94+
await componentsTab.waitForDisplayed({timeout: 5000});
95+
const dump = await dumpVisualTree('accessibility-base-view-2');
96+
expect(dump).toMatchSnapshot();
97+
});
98+
test('Accessibility data for Role, Setsize etc.', async () => {
99+
await searchBox('Lab');
100+
const componentsTab = await app.findElementByTestID(
101+
'accessibility-base-view-3',
102+
);
103+
await componentsTab.waitForDisplayed({timeout: 5000});
104+
const dump = await dumpVisualTree('accessibility-base-view-3');
105+
expect(dump).toMatchSnapshot();
106+
});
80107
});

packages/e2e-test-app-fabric/test/__snapshots__/AccessibilityTest.test.ts.snap

+90
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,95 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`Accessibility Tests Accessibility data for Label and Level 1`] = `
4+
{
5+
"Automation Tree": {
6+
"AutomationId": "accessibility-base-view-2",
7+
"ControlType": 50026,
8+
"Level": 2,
9+
"LocalizedControlType": "group",
10+
"Name": "A hint for the red box.",
11+
},
12+
"Component Tree": {
13+
"Type": "Microsoft.ReactNative.Composition.ViewComponentView",
14+
"_Props": {
15+
"AccessibilityLabel": "A hint for the red box.",
16+
"TestId": "accessibility-base-view-2",
17+
},
18+
},
19+
"Visual Tree": {
20+
"Brush": {
21+
"Brush Type": "ColorBrush",
22+
"Color": "rgba(255, 0, 0, 255)",
23+
},
24+
"Comment": "accessibility-base-view-2",
25+
"Offset": "0, 0, 0",
26+
"Size": "50, 50",
27+
"Visual Type": "SpriteVisual",
28+
},
29+
}
30+
`;
31+
32+
exports[`Accessibility Tests Accessibility data for Label,Level and Hint 1`] = `
33+
{
34+
"Automation Tree": {
35+
"AutomationId": "accessibility-base-view-1",
36+
"ControlType": 50026,
37+
"HelpText": "A hint for the blue box.",
38+
"Level": 1,
39+
"LocalizedControlType": "group",
40+
"Name": "A blue box",
41+
},
42+
"Component Tree": {
43+
"Type": "Microsoft.ReactNative.Composition.ViewComponentView",
44+
"_Props": {
45+
"AccessibilityLabel": "A blue box",
46+
"TestId": "accessibility-base-view-1",
47+
},
48+
},
49+
"Visual Tree": {
50+
"Brush": {
51+
"Brush Type": "ColorBrush",
52+
"Color": "rgba(0, 0, 255, 255)",
53+
},
54+
"Comment": "accessibility-base-view-1",
55+
"Offset": "0, 0, 0",
56+
"Size": "50, 50",
57+
"Visual Type": "SpriteVisual",
58+
},
59+
}
60+
`;
61+
62+
exports[`Accessibility Tests Accessibility data for Role, Setsize etc. 1`] = `
63+
{
64+
"Automation Tree": {
65+
"AutomationId": "accessibility-base-view-3",
66+
"ControlType": 50007,
67+
"Level": 9,
68+
"LocalizedControlType": "list item",
69+
"Name": "Aria label takes precedence",
70+
"PositionInSet": 2,
71+
"SizeofSet": 5,
72+
},
73+
"Component Tree": {
74+
"Type": "Microsoft.ReactNative.Composition.ViewComponentView",
75+
"_Props": {
76+
"AccessibilityLabel": "Aria label takes precedence",
77+
"TestId": "accessibility-base-view-3",
78+
},
79+
},
80+
"Visual Tree": {
81+
"Brush": {
82+
"Brush Type": "ColorBrush",
83+
"Color": "rgba(255, 0, 0, 255)",
84+
},
85+
"Comment": "accessibility-base-view-3",
86+
"Offset": "0, 0, 0",
87+
"Size": "50, 50",
88+
"Visual Type": "SpriteVisual",
89+
},
90+
}
91+
`;
92+
393
exports[`Accessibility Tests Components can store range data by setting the min, max, and now of accessibilityValue 1`] = `
494
{
595
"Automation Tree": {

packages/e2e-test-app-fabric/test/__snapshots__/snapshotPages.test.js.snap

+8-1
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,23 @@ exports[`snapshotAllPages Accessibility Windows 1`] = `
1010
<View
1111
accessibilityHint="A hint for the blue box."
1212
accessibilityLabel="A blue box"
13+
accessibilityLevel={1}
14+
accessible={true}
1315
style={
1416
{
1517
"backgroundColor": "blue",
1618
"height": 50,
1719
"width": 50,
1820
}
1921
}
22+
testID="accessibility-base-view-1"
2023
/>
2124
<Text>
2225
The following has accessible and accessibilityLabel:
2326
</Text>
2427
<View
2528
accessibilityLabel="A hint for the red box."
29+
accessibilityLevel={2}
2630
accessible={true}
2731
style={
2832
{
@@ -31,25 +35,28 @@ exports[`snapshotAllPages Accessibility Windows 1`] = `
3135
"width": 50,
3236
}
3337
}
38+
testID="accessibility-base-view-2"
3439
/>
3540
<Text>
3641
The following has accessibilitySetSize, accessibilityPosInSet and accessibilityLabel:
3742
</Text>
3843
<View
3944
accessibilityLabel="This label should not be used"
40-
accessibilityLevel={4}
45+
accessibilityLevel={5}
4146
accessibilityPosInSet={2}
4247
accessibilityRole="listitem"
4348
accessibilitySetSize={5}
4449
accessible={true}
4550
aria-label="Aria label takes precedence"
51+
aria-level={9}
4652
style={
4753
{
4854
"backgroundColor": "red",
4955
"height": 50,
5056
"width": 50,
5157
}
5258
}
59+
testID="accessibility-base-view-3"
5360
/>
5461
</View>
5562
`;

packages/e2e-test-app-fabric/windows/RNTesterApp-Fabric/RNTesterApp-Fabric.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ winrt::Windows::Data::Json::JsonObject DumpUIATreeRecurse(
494494
BSTR name;
495495
int positionInSet = 0;
496496
int sizeOfSet = 0;
497+
int level = 0;
497498
LiveSetting liveSetting = LiveSetting::Off;
498499
BSTR itemStatus;
499500

@@ -511,6 +512,7 @@ winrt::Windows::Data::Json::JsonObject DumpUIATreeRecurse(
511512
pTarget4->get_CurrentPositionInSet(&positionInSet);
512513
pTarget4->get_CurrentSizeOfSet(&sizeOfSet);
513514
pTarget4->get_CurrentLiveSetting(&liveSetting);
515+
pTarget4->get_CurrentLevel(&level);
514516
pTarget4->Release();
515517
}
516518
result.Insert(L"AutomationId", winrt::Windows::Data::Json::JsonValue::CreateStringValue(automationId));
@@ -523,6 +525,7 @@ winrt::Windows::Data::Json::JsonObject DumpUIATreeRecurse(
523525
InsertStringValueIfNotEmpty(result, L"Name", name);
524526
InsertIntValueIfNotDefault(result, L"PositionInSet", positionInSet);
525527
InsertIntValueIfNotDefault(result, L"SizeofSet", sizeOfSet);
528+
InsertIntValueIfNotDefault(result, L"Level", level);
526529
InsertLiveSettingValueIfNotDefault(result, L"LiveSetting", liveSetting);
527530
InsertStringValueIfNotEmpty(result, L"ItemStatus", itemStatus);
528531
DumpUIAPatternInfo(pTarget, result);

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,11 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPropertyValue(PROPERT
559559
: SysAllocString(L"");
560560
break;
561561
}
562+
case UIA_LevelPropertyId: {
563+
pRetVal->vt = VT_I4;
564+
pRetVal->lVal = props->accessibilityLevel;
565+
break;
566+
}
562567
}
563568

564569
return hr;

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,9 @@ void ComponentView::updateAccessibilityProps(
804804
oldViewProps.accessibilityLiveRegion,
805805
newViewProps.accessibilityLiveRegion);
806806

807+
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
808+
EnsureUiaProvider(), UIA_LevelPropertyId, oldViewProps.accessibilityLevel, newViewProps.accessibilityLevel);
809+
807810
if ((oldViewProps.accessibilityState.has_value() && oldViewProps.accessibilityState->selected.has_value()) !=
808811
((newViewProps.accessibilityState.has_value() && newViewProps.accessibilityState->selected.has_value()))) {
809812
auto compProvider =

vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ HostPlatformViewProps::HostPlatformViewProps(
4040
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
4141
? sourceProps.accessibilitySetSize
4242
: convertRawProp(context, rawProps, "accessibilitySetSize", sourceProps.accessibilitySetSize, 0)),
43+
accessibilityLevel(
44+
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
45+
? sourceProps.accessibilityLevel
46+
: convertRawProp(context, rawProps, "accessibilityLevel", sourceProps.accessibilityLevel, 0)),
4347
accessibilityLiveRegion(
4448
ReactNativeFeatureFlags::enableCppPropsIteratorSetter() ? sourceProps.accessibilityLiveRegion
4549
: convertRawProp(
@@ -84,6 +88,7 @@ void HostPlatformViewProps::setProp(
8488
RAW_SET_PROP_SWITCH_CASE_BASIC(focusable);
8589
RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityPosInSet);
8690
RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilitySetSize);
91+
RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLevel);
8792
RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLiveRegion);
8893
RAW_SET_PROP_SWITCH_CASE_BASIC(keyDownEvents);
8994
RAW_SET_PROP_SWITCH_CASE_BASIC(keyUpEvents);

vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.h

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class HostPlatformViewProps : public BaseViewProps {
2727
int accessibilityPosInSet{0};
2828
int accessibilitySetSize{0};
2929
std::string accessibilityLiveRegion{"none"};
30+
int accessibilityLevel{0};
3031

3132
// std::optional<std::string> overflowAnchor{};
3233
std::optional<std::string> tooltip{};

0 commit comments

Comments
 (0)