Skip to content

Commit

Permalink
fix: add accessibility props to NativeStack screens (#11022)
Browse files Browse the repository at this point in the history
**Motivation**

Current Stack navigator (and other navigations) have accessibility props
like `importantForAccessbility` & `accessibilityElementsHidden` for
screens that are not currently visible on the screen. However,
NativeStack navigator does not have it, which breaks new React Native
Testing Library
[`includeHiddenElements`](https://callstack.github.io/react-native-testing-library/docs/api-queries#includehiddenelements-option)
query options which allows for ignoring elements hidden from
accessibility.

As far as I understand this works correctly with iOS/Android screen
readers because of using native primitives which are understood by
screen readers/assistive tech. This PR includes the two accessibility
props which should cause no harm to React Navigation user but would
support RNTL option.

**Test plan**

Change does not affect the UI or API, it only applies to adding
`importantForAccessbility` & `accessibilityElementsHidden` a11y props to
NativeStack screens.

The change must pass lint, typescript and tests.
  • Loading branch information
mdjastrzebski authored Nov 21, 2022
1 parent 9041069 commit 3ab05af
Show file tree
Hide file tree
Showing 13 changed files with 86 additions and 33 deletions.
2 changes: 1 addition & 1 deletion packages/bottom-tabs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
},
"devDependencies": {
"@react-navigation/native": "^6.0.13",
"@testing-library/react-native": "^7.2.0",
"@testing-library/react-native": "^11.5.0",
"@types/color": "^3.0.1",
"@types/react": "~18.0.0",
"@types/react-native": "~0.69.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"use-latest-callback": "^0.1.5"
},
"devDependencies": {
"@testing-library/react-native": "^7.2.0",
"@testing-library/react-native": "^11.5.0",
"@types/react": "~18.0.0",
"@types/react-is": "^17.0.0",
"del-cli": "^3.0.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
},
"devDependencies": {
"@react-navigation/core": "^6.4.0",
"@testing-library/react-native": "^7.2.0",
"@testing-library/react-native": "^11.5.0",
"@types/deep-equal": "^1.0.1",
"@types/react": "~18.0.0",
"del-cli": "^3.0.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/drawer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
},
"devDependencies": {
"@react-navigation/native": "^6.0.13",
"@testing-library/react-native": "^7.2.0",
"@testing-library/react-native": "^11.5.0",
"@types/react": "~18.0.0",
"@types/react-native": "~0.69.1",
"del-cli": "^3.0.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/elements/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"devDependencies": {
"@react-native-masked-view/masked-view": "0.2.7",
"@react-navigation/native": "^6.0.13",
"@testing-library/react-native": "^7.2.0",
"@testing-library/react-native": "^11.5.0",
"@types/react": "~18.0.0",
"@types/react-native": "~0.69.1",
"del-cli": "^3.0.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/material-bottom-tabs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
},
"devDependencies": {
"@react-navigation/native": "^6.0.13",
"@testing-library/react-native": "^7.2.0",
"@testing-library/react-native": "^11.5.0",
"@types/react": "~18.0.0",
"@types/react-native": "~0.69.1",
"@types/react-native-vector-icons": "^6.4.10",
Expand Down
2 changes: 1 addition & 1 deletion packages/material-top-tabs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
},
"devDependencies": {
"@react-navigation/native": "^6.0.13",
"@testing-library/react-native": "^7.2.0",
"@testing-library/react-native": "^11.5.0",
"@types/react": "~18.0.0",
"@types/react-native": "~0.69.1",
"del-cli": "^3.0.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/native-stack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
},
"devDependencies": {
"@react-navigation/native": "^6.0.13",
"@testing-library/react-native": "^7.2.0",
"@testing-library/react-native": "^11.5.0",
"@types/react": "~18.0.0",
"@types/react-native": "~0.69.1",
"del-cli": "^3.0.1",
Expand Down
22 changes: 17 additions & 5 deletions packages/native-stack/src/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useHeaderHeight } from '@react-navigation/elements';
import { NavigationContainer, ParamListBase } from '@react-navigation/native';
import { fireEvent, render } from '@testing-library/react-native';
import {
fireEvent,
isHiddenFromAccessibility,
render,
} from '@testing-library/react-native';
import * as React from 'react';
import { Button, Platform, Text, View } from 'react-native';

Expand All @@ -18,6 +22,13 @@ jest.mock('react-native-safe-area-context', () => ({
}),
}));

/**
* Check if the element is "visible" (not hidden) from accessibility.
*/
const isVisible = (element: any) => {
return !isHiddenFromAccessibility(element);
};

afterEach(() => {
jest.resetAllMocks();
});
Expand All @@ -36,7 +47,7 @@ it('renders a native-stack navigator with screens', async () => {

const Stack = createNativeStackNavigator();

const { findByText, queryByText } = render(
const { getByText, queryByText } = render(
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="A" component={Test} />
Expand All @@ -45,12 +56,13 @@ it('renders a native-stack navigator with screens', async () => {
</NavigationContainer>
);

expect(queryByText('Screen A')).not.toBeNull();
expect(isVisible(getByText('Screen A'))).toBe(true);
expect(queryByText('Screen B')).toBeNull();

fireEvent.press(await findByText(/go to b/i));
fireEvent.press(getByText(/go to b/i));

expect(queryByText('Screen B')).not.toBeNull();
expect(isVisible(getByText('Screen A'))).toBe(false);
expect(isVisible(getByText('Screen B'))).toBe(true);
});

describe('useHeaderHeight in native-stack', () => {
Expand Down
14 changes: 12 additions & 2 deletions packages/native-stack/src/views/NativeStackView.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ const MaybeNestedStack = ({

type SceneViewProps = {
index: number;
focused: boolean;
descriptor: NativeStackDescriptor;
previousDescriptor?: NativeStackDescriptor;
nextDescriptor?: NativeStackDescriptor;
Expand All @@ -128,10 +129,11 @@ type SceneViewProps = {
};

const SceneView = ({
index,
focused,
descriptor,
previousDescriptor,
nextDescriptor,
index,
onWillDisappear,
onAppear,
onDisappear,
Expand Down Expand Up @@ -314,7 +316,13 @@ const SceneView = ({
headerTopInsetEnabled={headerTopInsetEnabled}
canGoBack={headerBack !== undefined}
/>
<View style={styles.scene}>
<View
accessibilityElementsHidden={!focused}
importantForAccessibility={
focused ? 'auto' : 'no-hide-descendants'
}
style={styles.scene}
>
<MaybeNestedStack
options={options}
route={route}
Expand Down Expand Up @@ -365,6 +373,7 @@ function NativeStackViewInner({ state, navigation, descriptors }: Props) {
<ScreenStack style={styles.container}>
{state.routes.map((route, index) => {
const descriptor = descriptors[route.key];
const isFocused = state.index === index;
const previousKey = state.routes[index - 1]?.key;
const nextKey = state.routes[index + 1]?.key;
const previousDescriptor = previousKey
Expand All @@ -376,6 +385,7 @@ function NativeStackViewInner({ state, navigation, descriptors }: Props) {
<SceneView
key={route.key}
index={index}
focused={isFocused}
descriptor={descriptor}
previousDescriptor={previousDescriptor}
nextDescriptor={nextDescriptor}
Expand Down
2 changes: 1 addition & 1 deletion packages/native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"nanoid": "^3.1.23"
},
"devDependencies": {
"@testing-library/react-native": "^7.2.0",
"@testing-library/react-native": "^11.5.0",
"@types/react": "~18.0.0",
"@types/react-dom": "~18.0.0",
"@types/react-native": "~0.69.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/stack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
},
"devDependencies": {
"@react-navigation/native": "^6.0.13",
"@testing-library/react-native": "^7.2.0",
"@testing-library/react-native": "^11.5.0",
"@types/color": "^3.0.1",
"@types/react": "~18.0.0",
"@types/react-native": "~0.69.1",
Expand Down
63 changes: 47 additions & 16 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3781,6 +3781,15 @@ __metadata:
languageName: node
linkType: hard

"@jest/schemas@npm:^29.0.0":
version: 29.0.0
resolution: "@jest/schemas@npm:29.0.0"
dependencies:
"@sinclair/typebox": ^0.24.1
checksum: 41355c78f09eb1097e57a3c5d0ca11c9099e235e01ea5fa4e3953562a79a6a9296c1d300f1ba50ca75236048829e056b00685cd2f1ff8285e56fd2ce01249acb
languageName: node
linkType: hard

"@jest/source-map@npm:^26.6.2":
version: 26.6.2
resolution: "@jest/source-map@npm:26.6.2"
Expand Down Expand Up @@ -5977,7 +5986,7 @@ __metadata:
dependencies:
"@react-navigation/elements": ^1.3.6
"@react-navigation/native": ^6.0.13
"@testing-library/react-native": ^7.2.0
"@testing-library/react-native": ^11.5.0
"@types/color": ^3.0.1
"@types/react": ~18.0.0
"@types/react-native": ~0.69.1
Expand All @@ -6004,7 +6013,7 @@ __metadata:
resolution: "@react-navigation/core@workspace:packages/core"
dependencies:
"@react-navigation/routers": ^6.1.3
"@testing-library/react-native": ^7.2.0
"@testing-library/react-native": ^11.5.0
"@types/react": ~18.0.0
"@types/react-is": ^17.0.0
del-cli: ^3.0.1
Expand All @@ -6028,7 +6037,7 @@ __metadata:
resolution: "@react-navigation/devtools@workspace:packages/devtools"
dependencies:
"@react-navigation/core": ^6.4.0
"@testing-library/react-native": ^7.2.0
"@testing-library/react-native": ^11.5.0
"@types/deep-equal": ^1.0.1
"@types/react": ~18.0.0
deep-equal: ^2.0.5
Expand All @@ -6050,7 +6059,7 @@ __metadata:
dependencies:
"@react-navigation/elements": ^1.3.6
"@react-navigation/native": ^6.0.13
"@testing-library/react-native": ^7.2.0
"@testing-library/react-native": ^11.5.0
"@types/react": ~18.0.0
"@types/react-native": ~0.69.1
color: ^4.2.3
Expand Down Expand Up @@ -6081,7 +6090,7 @@ __metadata:
dependencies:
"@react-native-masked-view/masked-view": 0.2.7
"@react-navigation/native": ^6.0.13
"@testing-library/react-native": ^7.2.0
"@testing-library/react-native": ^11.5.0
"@types/react": ~18.0.0
"@types/react-native": ~0.69.1
del-cli: ^3.0.1
Expand Down Expand Up @@ -6163,7 +6172,7 @@ __metadata:
dependencies:
"@react-navigation/elements": ^1.3.6
"@react-navigation/native": ^6.0.13
"@testing-library/react-native": ^7.2.0
"@testing-library/react-native": ^11.5.0
"@types/react": ~18.0.0
"@types/react-native": ~0.69.1
"@types/react-native-vector-icons": ^6.4.10
Expand All @@ -6190,7 +6199,7 @@ __metadata:
resolution: "@react-navigation/material-top-tabs@workspace:packages/material-top-tabs"
dependencies:
"@react-navigation/native": ^6.0.13
"@testing-library/react-native": ^7.2.0
"@testing-library/react-native": ^11.5.0
"@types/react": ~18.0.0
"@types/react-native": ~0.69.1
color: ^4.2.3
Expand All @@ -6217,7 +6226,7 @@ __metadata:
dependencies:
"@react-navigation/elements": ^1.3.6
"@react-navigation/native": ^6.0.13
"@testing-library/react-native": ^7.2.0
"@testing-library/react-native": ^11.5.0
"@types/react": ~18.0.0
"@types/react-native": ~0.69.1
del-cli: ^3.0.1
Expand All @@ -6241,7 +6250,7 @@ __metadata:
resolution: "@react-navigation/native@workspace:packages/native"
dependencies:
"@react-navigation/core": ^6.4.0
"@testing-library/react-native": ^7.2.0
"@testing-library/react-native": ^11.5.0
"@types/react": ~18.0.0
"@types/react-dom": ~18.0.0
"@types/react-native": ~0.69.1
Expand Down Expand Up @@ -6277,7 +6286,7 @@ __metadata:
dependencies:
"@react-navigation/elements": ^1.3.6
"@react-navigation/native": ^6.0.13
"@testing-library/react-native": ^7.2.0
"@testing-library/react-native": ^11.5.0
"@types/color": ^3.0.1
"@types/react": ~18.0.0
"@types/react-native": ~0.69.1
Expand Down Expand Up @@ -6334,6 +6343,13 @@ __metadata:
languageName: node
linkType: hard

"@sinclair/typebox@npm:^0.24.1":
version: 0.24.51
resolution: "@sinclair/typebox@npm:0.24.51"
checksum: fd0d855e748ef767eb19da1a60ed0ab928e91e0f358c1dd198d600762c0015440b15755e96d1176e2a0db7e09c6a64ed487828ee10dd0c3e22f61eb09c478cd0
languageName: node
linkType: hard

"@sindresorhus/is@npm:^0.14.0":
version: 0.14.0
resolution: "@sindresorhus/is@npm:0.14.0"
Expand Down Expand Up @@ -6391,16 +6407,20 @@ __metadata:
languageName: node
linkType: hard

"@testing-library/react-native@npm:^7.2.0":
version: 7.2.0
resolution: "@testing-library/react-native@npm:7.2.0"
"@testing-library/react-native@npm:^11.5.0":
version: 11.5.0
resolution: "@testing-library/react-native@npm:11.5.0"
dependencies:
pretty-format: ^26.0.1
pretty-format: ^29.0.0
peerDependencies:
jest: ">=28.0.0"
react: ">=16.0.0"
react-native: ">=0.59"
react-test-renderer: ">=16.0.0"
checksum: 9cc50fbce6003131e62e96291445a19b6bb68889ed5a0167d988cd02cffe373febc52830cfdf033dc914b63bf850cbb1c1884dfd196cbc8c22536a5889fb47ee
peerDependenciesMeta:
jest:
optional: true
checksum: 841c769c2734d7fb7d9a4054fad0582505e49c4c2d99ddcf742ef11f6dd6ee3bcf040400e27dbb55d7fda48b305ae92f40dc8c507e93dffb96d768ff3aba7bad
languageName: node
linkType: hard

Expand Down Expand Up @@ -22645,7 +22665,7 @@ __metadata:
languageName: node
linkType: hard

"pretty-format@npm:^26.0.0, pretty-format@npm:^26.0.1, pretty-format@npm:^26.5.2, pretty-format@npm:^26.6.2":
"pretty-format@npm:^26.0.0, pretty-format@npm:^26.5.2, pretty-format@npm:^26.6.2":
version: 26.6.2
resolution: "pretty-format@npm:26.6.2"
dependencies:
Expand All @@ -22668,6 +22688,17 @@ __metadata:
languageName: node
linkType: hard

"pretty-format@npm:^29.0.0":
version: 29.3.1
resolution: "pretty-format@npm:29.3.1"
dependencies:
"@jest/schemas": ^29.0.0
ansi-styles: ^5.0.0
react-is: ^18.0.0
checksum: 9917a0bb859cd7a24a343363f70d5222402c86d10eb45bcc2f77b23a4e67586257390e959061aec22762a782fe6bafb59bf34eb94527bc2e5d211afdb287eb4e
languageName: node
linkType: hard

"printj@npm:~1.3.1":
version: 1.3.1
resolution: "printj@npm:1.3.1"
Expand Down

0 comments on commit 3ab05af

Please sign in to comment.