Skip to content

Implement accessibilityLevel for Fabric #14593

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

Merged
merged 8 commits into from
Apr 24, 2025
Merged
Show file tree
Hide file tree
Changes from all 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,7 @@
{
"type": "prerelease",
"comment": "Implement accessibilityLevel for Fabric",
"packageName": "react-native-windows",
"email": "kvineeth@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,35 @@ class AccessibilityBaseExample extends React.Component {
<Text>The following has accessibilityLabel and accessibilityHint:</Text>
<View
style={{width: 50, height: 50, backgroundColor: 'blue'}}
accessible={true}
accessibilityLabel="A blue box"
accessibilityHint="A hint for the blue box."
accessibilityLevel={1}
testID="accessibility-base-view-1"
/>
<Text>The following has accessible and accessibilityLabel:</Text>
<View
style={{width: 50, height: 50, backgroundColor: 'red'}}
accessible={true}
accessibilityLabel="A hint for the red box."
accessibilityLevel={2}
testID="accessibility-base-view-2"
/>
<Text>
The following has accessibilitySetSize, accessibilityPosInSet and
accessibilityLabel:
</Text>
<View
style={{width: 50, height: 50, backgroundColor: 'red'}}
testID="accessibility-base-view-3"
accessible={true}
accessibilityRole="listitem"
accessibilityLabel="This label should not be used"
aria-label="Aria label takes precedence"
accessibilitySetSize={5}
accessibilityPosInSet={2}
accessibilityLevel={4}
aria-level={9} //aria-level takes precedence over accessibilityLevel
accessibilityLevel={5}
/>
</View>
);
Expand Down
27 changes: 27 additions & 0 deletions packages/e2e-test-app-fabric/test/AccessibilityTest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,31 @@ describe('Accessibility Tests', () => {
const dump = await dumpVisualTree('accessibilityValue-text');
expect(dump).toMatchSnapshot();
});
test('Accessibility data for Label,Level and Hint', async () => {
await searchBox('Lab');
const componentsTab = await app.findElementByTestID(
'accessibility-base-view-1',
);
await componentsTab.waitForDisplayed({timeout: 5000});
const dump = await dumpVisualTree('accessibility-base-view-1');
expect(dump).toMatchSnapshot();
});
test('Accessibility data for Label and Level', async () => {
await searchBox('Lab');
const componentsTab = await app.findElementByTestID(
'accessibility-base-view-2',
);
await componentsTab.waitForDisplayed({timeout: 5000});
const dump = await dumpVisualTree('accessibility-base-view-2');
expect(dump).toMatchSnapshot();
});
test('Accessibility data for Role, Setsize etc.', async () => {
await searchBox('Lab');
const componentsTab = await app.findElementByTestID(
'accessibility-base-view-3',
);
await componentsTab.waitForDisplayed({timeout: 5000});
const dump = await dumpVisualTree('accessibility-base-view-3');
expect(dump).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,95 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Accessibility Tests Accessibility data for Label and Level 1`] = `
{
"Automation Tree": {
"AutomationId": "accessibility-base-view-2",
"ControlType": 50026,
"Level": 2,
"LocalizedControlType": "group",
"Name": "A hint for the red box.",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ViewComponentView",
"_Props": {
"AccessibilityLabel": "A hint for the red box.",
"TestId": "accessibility-base-view-2",
},
},
"Visual Tree": {
"Brush": {
"Brush Type": "ColorBrush",
"Color": "rgba(255, 0, 0, 255)",
},
"Comment": "accessibility-base-view-2",
"Offset": "0, 0, 0",
"Size": "50, 50",
"Visual Type": "SpriteVisual",
},
}
`;

exports[`Accessibility Tests Accessibility data for Label,Level and Hint 1`] = `
{
"Automation Tree": {
"AutomationId": "accessibility-base-view-1",
"ControlType": 50026,
"HelpText": "A hint for the blue box.",
"Level": 1,
"LocalizedControlType": "group",
"Name": "A blue box",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ViewComponentView",
"_Props": {
"AccessibilityLabel": "A blue box",
"TestId": "accessibility-base-view-1",
},
},
"Visual Tree": {
"Brush": {
"Brush Type": "ColorBrush",
"Color": "rgba(0, 0, 255, 255)",
},
"Comment": "accessibility-base-view-1",
"Offset": "0, 0, 0",
"Size": "50, 50",
"Visual Type": "SpriteVisual",
},
}
`;

exports[`Accessibility Tests Accessibility data for Role, Setsize etc. 1`] = `
{
"Automation Tree": {
"AutomationId": "accessibility-base-view-3",
"ControlType": 50007,
"Level": 9,
"LocalizedControlType": "list item",
"Name": "Aria label takes precedence",
"PositionInSet": 2,
"SizeofSet": 5,
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ViewComponentView",
"_Props": {
"AccessibilityLabel": "Aria label takes precedence",
"TestId": "accessibility-base-view-3",
},
},
"Visual Tree": {
"Brush": {
"Brush Type": "ColorBrush",
"Color": "rgba(255, 0, 0, 255)",
},
"Comment": "accessibility-base-view-3",
"Offset": "0, 0, 0",
"Size": "50, 50",
"Visual Type": "SpriteVisual",
},
}
`;

exports[`Accessibility Tests Components can store range data by setting the min, max, and now of accessibilityValue 1`] = `
{
"Automation Tree": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,23 @@ exports[`snapshotAllPages Accessibility Windows 1`] = `
<View
accessibilityHint="A hint for the blue box."
accessibilityLabel="A blue box"
accessibilityLevel={1}
accessible={true}
style={
{
"backgroundColor": "blue",
"height": 50,
"width": 50,
}
}
testID="accessibility-base-view-1"
/>
<Text>
The following has accessible and accessibilityLabel:
</Text>
<View
accessibilityLabel="A hint for the red box."
accessibilityLevel={2}
accessible={true}
style={
{
Expand All @@ -31,25 +35,28 @@ exports[`snapshotAllPages Accessibility Windows 1`] = `
"width": 50,
}
}
testID="accessibility-base-view-2"
/>
<Text>
The following has accessibilitySetSize, accessibilityPosInSet and accessibilityLabel:
</Text>
<View
accessibilityLabel="This label should not be used"
accessibilityLevel={4}
accessibilityLevel={5}
accessibilityPosInSet={2}
accessibilityRole="listitem"
accessibilitySetSize={5}
accessible={true}
aria-label="Aria label takes precedence"
aria-level={9}
style={
{
"backgroundColor": "red",
"height": 50,
"width": 50,
}
}
testID="accessibility-base-view-3"
/>
</View>
`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ winrt::Windows::Data::Json::JsonObject DumpUIATreeRecurse(
BSTR name;
int positionInSet = 0;
int sizeOfSet = 0;
int level = 0;
LiveSetting liveSetting = LiveSetting::Off;
BSTR itemStatus;

Expand All @@ -511,6 +512,7 @@ winrt::Windows::Data::Json::JsonObject DumpUIATreeRecurse(
pTarget4->get_CurrentPositionInSet(&positionInSet);
pTarget4->get_CurrentSizeOfSet(&sizeOfSet);
pTarget4->get_CurrentLiveSetting(&liveSetting);
pTarget4->get_CurrentLevel(&level);
pTarget4->Release();
}
result.Insert(L"AutomationId", winrt::Windows::Data::Json::JsonValue::CreateStringValue(automationId));
Expand All @@ -523,6 +525,7 @@ winrt::Windows::Data::Json::JsonObject DumpUIATreeRecurse(
InsertStringValueIfNotEmpty(result, L"Name", name);
InsertIntValueIfNotDefault(result, L"PositionInSet", positionInSet);
InsertIntValueIfNotDefault(result, L"SizeofSet", sizeOfSet);
InsertIntValueIfNotDefault(result, L"Level", level);
InsertLiveSettingValueIfNotDefault(result, L"LiveSetting", liveSetting);
InsertStringValueIfNotEmpty(result, L"ItemStatus", itemStatus);
DumpUIAPatternInfo(pTarget, result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,11 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPropertyValue(PROPERT
: SysAllocString(L"");
break;
}
case UIA_LevelPropertyId: {
pRetVal->vt = VT_I4;
pRetVal->lVal = props->accessibilityLevel;
break;
}
}

return hr;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,9 @@ void ComponentView::updateAccessibilityProps(
oldViewProps.accessibilityLiveRegion,
newViewProps.accessibilityLiveRegion);

winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
EnsureUiaProvider(), UIA_LevelPropertyId, oldViewProps.accessibilityLevel, newViewProps.accessibilityLevel);

if ((oldViewProps.accessibilityState.has_value() && oldViewProps.accessibilityState->selected.has_value()) !=
((newViewProps.accessibilityState.has_value() && newViewProps.accessibilityState->selected.has_value()))) {
auto compProvider =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ HostPlatformViewProps::HostPlatformViewProps(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.accessibilitySetSize
: convertRawProp(context, rawProps, "accessibilitySetSize", sourceProps.accessibilitySetSize, 0)),
accessibilityLevel(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.accessibilityLevel
: convertRawProp(context, rawProps, "accessibilityLevel", sourceProps.accessibilityLevel, 0)),
accessibilityLiveRegion(
ReactNativeFeatureFlags::enableCppPropsIteratorSetter() ? sourceProps.accessibilityLiveRegion
: convertRawProp(
Expand Down Expand Up @@ -84,6 +88,7 @@ void HostPlatformViewProps::setProp(
RAW_SET_PROP_SWITCH_CASE_BASIC(focusable);
RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityPosInSet);
RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilitySetSize);
RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLevel);
RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLiveRegion);
RAW_SET_PROP_SWITCH_CASE_BASIC(keyDownEvents);
RAW_SET_PROP_SWITCH_CASE_BASIC(keyUpEvents);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class HostPlatformViewProps : public BaseViewProps {
int accessibilityPosInSet{0};
int accessibilitySetSize{0};
std::string accessibilityLiveRegion{"none"};
int accessibilityLevel{0};

// std::optional<std::string> overflowAnchor{};
std::optional<std::string> tooltip{};
Expand Down