@react-native-ama/core 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/{core/src/components → components}/AMAProvider.js +1 -2
  2. package/dist/{core/src/index.js → index.js} +3 -3
  3. package/package.json +31 -6
  4. package/src/components/AMAProvider.d.ts +26 -0
  5. package/src/components/AMAProvider.integration.test.tsx +145 -0
  6. package/src/components/AMAProvider.js +147 -0
  7. package/src/components/AMAProvider.test.tsx +199 -0
  8. package/src/components/AMAProvider.tsx +229 -0
  9. package/src/components/AutofocusContainer.d.ts +9 -0
  10. package/src/components/AutofocusContainer.js +58 -0
  11. package/src/components/AutofocusContainer.test.tsx +69 -0
  12. package/src/components/AutofocusContainer.tsx +43 -0
  13. package/src/components/HideChildrenFromAccessibilityTree.d.ts +6 -0
  14. package/src/components/HideChildrenFromAccessibilityTree.js +40 -0
  15. package/src/components/HideChildrenFromAccessibilityTree.test.tsx +129 -0
  16. package/src/components/HideChildrenFromAccessibilityTree.tsx +55 -0
  17. package/src/hooks/useButtonChecks.d.ts +8 -0
  18. package/src/hooks/useButtonChecks.js +51 -0
  19. package/src/hooks/useButtonChecks.ts +73 -0
  20. package/src/hooks/useChecks.d.ts +15 -0
  21. package/src/hooks/useChecks.js +152 -0
  22. package/src/hooks/useChecks.test.ts +214 -0
  23. package/src/hooks/useChecks.ts +207 -0
  24. package/src/hooks/useFocus.d.ts +4 -0
  25. package/{dist/internal/src/utils/maybeGenerateStringFromElement.js → src/hooks/useFocus.js} +26 -22
  26. package/src/hooks/useFocus.test.ts +99 -0
  27. package/src/hooks/useFocus.ts +42 -0
  28. package/src/hooks/useTimedAction.android.test.ts +39 -0
  29. package/src/hooks/useTimedAction.d.ts +3 -0
  30. package/src/hooks/useTimedAction.ios.test.ts +46 -0
  31. package/src/hooks/useTimedAction.js +63 -0
  32. package/src/hooks/useTimedAction.ts +40 -0
  33. package/src/index.d.ts +7 -0
  34. package/src/index.js +20 -0
  35. package/src/index.ts +16 -0
  36. package/src/utils/numerify.test.ts +11 -0
  37. package/src/utils/numerify.ts +3 -0
  38. package/dist/internal/src/checks/checkAccessibilityRole.d.ts +0 -3
  39. package/dist/internal/src/checks/checkAccessibilityRole.js +0 -32
  40. package/dist/internal/src/checks/checkFocusTrap.d.ts +0 -8
  41. package/dist/internal/src/checks/checkFocusTrap.js +0 -34
  42. package/dist/internal/src/checks/checkForAccessibilityState.d.ts +0 -7
  43. package/dist/internal/src/checks/checkForAccessibilityState.js +0 -48
  44. package/dist/internal/src/checks/checkMinimumSize.d.ts +0 -3
  45. package/dist/internal/src/checks/checkMinimumSize.js +0 -29
  46. package/dist/internal/src/checks/contrastChecker.d.ts +0 -8
  47. package/dist/internal/src/checks/contrastChecker.js +0 -77
  48. package/dist/internal/src/checks/noUndefinedProperty.d.ts +0 -8
  49. package/dist/internal/src/checks/noUndefinedProperty.js +0 -14
  50. package/dist/internal/src/checks/uppercaseChecker.d.ts +0 -9
  51. package/dist/internal/src/checks/uppercaseChecker.js +0 -18
  52. package/dist/internal/src/checks/uppercaseStringChecker.d.ts +0 -8
  53. package/dist/internal/src/checks/uppercaseStringChecker.js +0 -29
  54. package/dist/internal/src/index.d.ts +0 -20
  55. package/dist/internal/src/index.js +0 -56
  56. package/dist/internal/src/types.d.ts +0 -17
  57. package/dist/internal/src/types.js +0 -2
  58. package/dist/internal/src/utils/applyStyle.d.ts +0 -10
  59. package/dist/internal/src/utils/applyStyle.js +0 -33
  60. package/dist/internal/src/utils/constants.d.ts +0 -3
  61. package/dist/internal/src/utils/constants.js +0 -32
  62. package/dist/internal/src/utils/error.style.d.ts +0 -6
  63. package/dist/internal/src/utils/error.style.js +0 -11
  64. package/dist/internal/src/utils/generateAccessibilityStateFromProp.d.ts +0 -9
  65. package/dist/internal/src/utils/generateAccessibilityStateFromProp.js +0 -35
  66. package/dist/internal/src/utils/getPropertyFromStyle.d.ts +0 -2
  67. package/dist/internal/src/utils/getPropertyFromStyle.js +0 -10
  68. package/dist/internal/src/utils/interpolateAnimationStates.d.ts +0 -2
  69. package/dist/internal/src/utils/interpolateAnimationStates.js +0 -43
  70. package/dist/internal/src/utils/isFocused.d.ts +0 -2
  71. package/dist/internal/src/utils/isFocused.js +0 -7
  72. package/dist/internal/src/utils/logger.d.ts +0 -16
  73. package/dist/internal/src/utils/logger.js +0 -53
  74. package/dist/internal/src/utils/logger.rules.d.ts +0 -15
  75. package/dist/internal/src/utils/logger.rules.js +0 -74
  76. package/dist/internal/src/utils/maybeGenerateStringFromElement.d.ts +0 -3
  77. package/dist/internal/src/utils/minimumTouchableSize.d.ts +0 -3
  78. package/dist/internal/src/utils/minimumTouchableSize.js +0 -9
  79. package/dist/{core/src/components → components}/AMAProvider.d.ts +0 -0
  80. package/dist/{core/src/components → components}/AutofocusContainer.d.ts +0 -0
  81. package/dist/{core/src/components → components}/AutofocusContainer.js +0 -0
  82. package/dist/{core/src/components → components}/HideChildrenFromAccessibilityTree.d.ts +0 -0
  83. package/dist/{core/src/components → components}/HideChildrenFromAccessibilityTree.js +0 -0
  84. package/dist/{core/src/hooks → hooks}/useButtonChecks.d.ts +0 -0
  85. package/dist/{core/src/hooks → hooks}/useButtonChecks.js +0 -0
  86. package/dist/{core/src/hooks → hooks}/useChecks.d.ts +0 -0
  87. package/dist/{core/src/hooks → hooks}/useChecks.js +0 -0
  88. package/dist/{core/src/hooks → hooks}/useFocus.d.ts +0 -0
  89. package/dist/{core/src/hooks → hooks}/useFocus.js +0 -0
  90. package/dist/{core/src/hooks → hooks}/useTimedAction.d.ts +0 -0
  91. package/dist/{core/src/hooks → hooks}/useTimedAction.js +0 -0
  92. package/dist/{core/src/index.d.ts → index.d.ts} +1 -1
  93. /package/dist/{core/src/utils → utils}/numerify.d.ts +0 -0
  94. /package/dist/{core/src/utils → utils}/numerify.js +0 -0
@@ -0,0 +1,229 @@
1
+ /* eslint-disable react-native/no-inline-styles */
2
+
3
+ import { RED } from '@react-native-ama/internal';
4
+ import * as React from 'react';
5
+ import {
6
+ AccessibilityChangeEventName,
7
+ AccessibilityInfo,
8
+ NativeEventSubscription,
9
+ Text,
10
+ View,
11
+ } from 'react-native';
12
+
13
+ type AMAProviderProps = {
14
+ children: React.ReactNode;
15
+ };
16
+
17
+ type SharedContextValue = {
18
+ isBoldTextEnabled: boolean;
19
+ isScreenReaderEnabled: boolean;
20
+ isGrayscaleEnabled: boolean;
21
+ isInvertColorsEnabled: boolean;
22
+ isReduceMotionEnabled: boolean;
23
+ isReduceTransparencyEnabled: boolean;
24
+ reactNavigationScreenOptions: {
25
+ animationEnabled: boolean;
26
+ animation: 'default' | 'fade';
27
+ };
28
+ };
29
+ export type AMADevContextValue = SharedContextValue & {
30
+ trackError: (id: string) => void;
31
+ removeError: (id: string) => void;
32
+ };
33
+
34
+ export type AMAProdContextValue = SharedContextValue & {};
35
+
36
+ export type AMAContextValue = AMADevContextValue | AMAProdContextValue;
37
+
38
+ type AccessibilityEvents = Exclude<AccessibilityChangeEventName, 'change'>;
39
+
40
+ type AccessibilityInfoKey = Exclude<
41
+ keyof AMAContextValue,
42
+ 'reactNavigationScreenOptions' | 'trackError' | 'removeError'
43
+ >;
44
+
45
+ type Extractor<S extends AccessibilityEvents> = S extends `${infer R}Changed`
46
+ ? R
47
+ : never;
48
+
49
+ type AccessibilityInfoEvents = {
50
+ [key in AccessibilityEvents]: `is${Capitalize<Extractor<key>>}Enabled`;
51
+ };
52
+
53
+ const eventsMapping: AccessibilityInfoEvents = {
54
+ reduceTransparencyChanged: 'isReduceTransparencyEnabled',
55
+ reduceMotionChanged: 'isReduceMotionEnabled',
56
+ grayscaleChanged: 'isGrayscaleEnabled',
57
+ boldTextChanged: 'isBoldTextEnabled',
58
+ invertColorsChanged: 'isInvertColorsEnabled',
59
+ screenReaderChanged: 'isScreenReaderEnabled',
60
+ };
61
+
62
+ export const isDevContextValue = (
63
+ value: AMAContextValue,
64
+ ): value is AMADevContextValue =>
65
+ (value as AMADevContextValue).trackError !== undefined;
66
+
67
+ const DEFAULT_VALUES = {
68
+ isReduceTransparencyEnabled: false,
69
+ isBoldTextEnabled: false,
70
+ isGrayscaleEnabled: false,
71
+ isInvertColorsEnabled: false,
72
+ isReduceMotionEnabled: false,
73
+ isScreenReaderEnabled: false,
74
+ reactNavigationScreenOptions: {
75
+ animationEnabled: true,
76
+ animation: 'default',
77
+ },
78
+ } satisfies AMAContextValue;
79
+
80
+ const AMAContext = React.createContext<AMAContextValue | null>(null);
81
+
82
+ export const AMAProvider: React.FC<AMAProviderProps> = ({ children }) => {
83
+ const [values, setValues] = React.useState<AMAContextValue>(DEFAULT_VALUES);
84
+
85
+ const handleAccessibilityInfoChanged = (key: AccessibilityInfoKey) => {
86
+ return (newValue: boolean) => {
87
+ setValues(oldValues => {
88
+ const newValues = { ...oldValues };
89
+ newValues[key] = newValue;
90
+
91
+ if (key === 'isReduceMotionEnabled') {
92
+ newValues.reactNavigationScreenOptions.animationEnabled = !newValue;
93
+ newValues.reactNavigationScreenOptions.animation = newValue
94
+ ? 'fade'
95
+ : 'default';
96
+ }
97
+
98
+ return {
99
+ ...oldValues,
100
+ ...newValues,
101
+ };
102
+ });
103
+ };
104
+ };
105
+
106
+ React.useEffect(() => {
107
+ const allInitPromises: Promise<boolean>[] = [];
108
+
109
+ const subscriptions: NativeEventSubscription[] = Object.entries(
110
+ eventsMapping,
111
+ ).map(([eventName, contextKey]) => {
112
+ allInitPromises.push(AccessibilityInfo[contextKey]());
113
+
114
+ return AccessibilityInfo.addEventListener(
115
+ eventName as AccessibilityEvents,
116
+ handleAccessibilityInfoChanged(contextKey),
117
+ );
118
+ });
119
+
120
+ Promise.all(allInitPromises).then(promisesValues => {
121
+ const newValues = Object.values(eventsMapping).reduce(
122
+ (list, key, index) => {
123
+ list[key] = promisesValues[index] as boolean;
124
+
125
+ return list;
126
+ },
127
+ {} as AMAContextValue,
128
+ );
129
+
130
+ setValues(oldValues => {
131
+ return {
132
+ ...oldValues,
133
+ ...newValues,
134
+ };
135
+ });
136
+ });
137
+
138
+ return () => {
139
+ subscriptions.forEach(subscription => subscription?.remove());
140
+ };
141
+ }, []);
142
+
143
+ const [failedItems, setFailedItems] = React.useState<string[]>([]);
144
+
145
+ if (__DEV__) {
146
+ const trackError = (id: string) => {
147
+ if (failedItems.includes(id)) {
148
+ return;
149
+ }
150
+
151
+ setFailedItems(items => [...items, id]);
152
+
153
+ AccessibilityInfo.announceForAccessibility(
154
+ "One or more component didn't pass the accessibility check, please check the console for more info",
155
+ );
156
+ };
157
+
158
+ const removeError = (id: string) => {
159
+ setFailedItems(items => {
160
+ const index = items.indexOf(id);
161
+
162
+ if (index >= 0) {
163
+ items.splice(index);
164
+
165
+ return [...items];
166
+ }
167
+
168
+ return items;
169
+ });
170
+ };
171
+
172
+ return (
173
+ <AMAContext.Provider
174
+ value={{
175
+ ...values,
176
+ trackError,
177
+ removeError,
178
+ }}>
179
+ <View style={{ flex: 1 }}>
180
+ <>
181
+ {children}
182
+ {failedItems.length > 0 ? (
183
+ <AMAError count={failedItems.length} />
184
+ ) : null}
185
+ </>
186
+ </View>
187
+ </AMAContext.Provider>
188
+ );
189
+ }
190
+
191
+ return <AMAContext.Provider value={values}>{children}</AMAContext.Provider>;
192
+ };
193
+
194
+ const AMAError = ({ count }: { count: number }) => {
195
+ const error = `${count} component(s) didn't pass the accessibility check(s)`;
196
+
197
+ return (
198
+ <View
199
+ accessibilityLabel={error}
200
+ accessibilityHint="Please check the console for more info..."
201
+ style={{
202
+ paddingHorizontal: 24,
203
+ paddingTop: 24,
204
+ paddingBottom: 48,
205
+ backgroundColor: RED,
206
+ }}
207
+ testID="amaError">
208
+ <View accessible={true}>
209
+ <Text
210
+ style={{ color: 'white', fontSize: 16, lineHeight: 26 }}
211
+ testID="amaError.message">
212
+ {error}
213
+ </Text>
214
+ <Text style={{ color: 'white', fontSize: 16, lineHeight: 24 }}>
215
+ Please check the console for more info...
216
+ </Text>
217
+ </View>
218
+ </View>
219
+ );
220
+ };
221
+
222
+ export const useAMAContext = () => {
223
+ const context = React.useContext(AMAContext);
224
+ if (!context) {
225
+ throw new Error('Please wrap your app with <AMAProvider />');
226
+ }
227
+
228
+ return context;
229
+ };
@@ -0,0 +1,9 @@
1
+ import * as React from 'react';
2
+ import { ViewProps } from 'react-native';
3
+ type AutofocusContainerProps = React.PropsWithChildren<({
4
+ wrapChildrenInAccessibleView?: true;
5
+ } & ViewProps) | {
6
+ wrapChildrenInAccessibleView: false;
7
+ }>;
8
+ export declare const AutofocusContainer: ({ children, wrapChildrenInAccessibleView, ...viewProps }: AutofocusContainerProps) => React.JSX.Element;
9
+ export {};
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __rest = (this && this.__rest) || function (s, e) {
26
+ var t = {};
27
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
28
+ t[p] = s[p];
29
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
30
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
31
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
32
+ t[p[i]] = s[p[i]];
33
+ }
34
+ return t;
35
+ };
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.AutofocusContainer = void 0;
38
+ const React = __importStar(require("react"));
39
+ const react_native_1 = require("react-native");
40
+ const useFocus_1 = require("../hooks/useFocus");
41
+ const AutofocusContainer = (_a) => {
42
+ var { children, wrapChildrenInAccessibleView = true } = _a, viewProps = __rest(_a, ["children", "wrapChildrenInAccessibleView"]);
43
+ const containerRef = React.useRef(null);
44
+ const { setFocus } = (0, useFocus_1.useFocus)();
45
+ React.useEffect(() => {
46
+ setTimeout(() => {
47
+ setFocus(containerRef.current);
48
+ }, 0);
49
+ }, [setFocus]);
50
+ return wrapChildrenInAccessibleView ? (<react_native_1.TouchableWithoutFeedback ref={containerRef}>
51
+ <react_native_1.View accessible={true} testID="autofocusContainer.accessibleView" {...viewProps}>
52
+ {children}
53
+ </react_native_1.View>
54
+ </react_native_1.TouchableWithoutFeedback>) : (<react_native_1.TouchableWithoutFeedback ref={containerRef}>
55
+ {children}
56
+ </react_native_1.TouchableWithoutFeedback>);
57
+ };
58
+ exports.AutofocusContainer = AutofocusContainer;
@@ -0,0 +1,69 @@
1
+ import { render, waitFor } from '@testing-library/react-native';
2
+ import * as React from 'react';
3
+
4
+ import * as UseFocus from '../hooks/useFocus';
5
+ import { AutofocusContainer } from './AutofocusContainer';
6
+
7
+ beforeEach(() => {
8
+ jest.clearAllMocks();
9
+ });
10
+
11
+ describe('AutofocusContainer', () => {
12
+ it('it call setFocus when gets rendered', async () => {
13
+ const setFocus = jest.fn();
14
+ jest.spyOn(UseFocus, 'useFocus').mockReturnValue({
15
+ setFocus,
16
+ });
17
+
18
+ render(
19
+ <AutofocusContainer>
20
+ <></>
21
+ </AutofocusContainer>,
22
+ );
23
+
24
+ await waitFor(() => expect(setFocus).toBeCalled());
25
+ });
26
+
27
+ it.each([undefined, true])(
28
+ 'wraps the children with an accessible View when wrapChildrenInAccessibleView is %s',
29
+ async () => {
30
+ const setFocus = jest.fn();
31
+ jest.spyOn(UseFocus, 'useFocus').mockReturnValue({
32
+ setFocus,
33
+ });
34
+
35
+ const renderAPI = render(
36
+ <AutofocusContainer wrapChildrenInAccessibleView={true}>
37
+ <></>
38
+ </AutofocusContainer>,
39
+ );
40
+
41
+ await waitFor(() => expect(setFocus).toBeCalled());
42
+
43
+ expect(
44
+ renderAPI.getByTestId('autofocusContainer.accessibleView'),
45
+ ).toBeDefined();
46
+ },
47
+ );
48
+
49
+ it('does not wrap the children with an accessible View when wrapChildrenInAccessibleView is false', async () => {
50
+ const setFocus = jest.fn();
51
+ jest.spyOn(UseFocus, 'useFocus').mockReturnValue({
52
+ setFocus,
53
+ });
54
+
55
+ const renderAPI = render(
56
+ <AutofocusContainer wrapChildrenInAccessibleView={false}>
57
+ <></>
58
+ </AutofocusContainer>,
59
+ );
60
+
61
+ await waitFor(() => expect(setFocus).toBeCalled());
62
+
63
+ expect(
64
+ renderAPI.queryByTestId('autofocusContainer.accessibleView'),
65
+ ).toBeNull();
66
+ });
67
+ });
68
+
69
+ jest.mock('../hooks/useFocus');
@@ -0,0 +1,43 @@
1
+ import * as React from 'react';
2
+ import { TouchableWithoutFeedback, View, ViewProps } from 'react-native';
3
+
4
+ import { useFocus } from '../hooks/useFocus';
5
+
6
+ type AutofocusContainerProps = React.PropsWithChildren<
7
+ | ({
8
+ wrapChildrenInAccessibleView?: true;
9
+ } & ViewProps)
10
+ | {
11
+ wrapChildrenInAccessibleView: false;
12
+ }
13
+ >;
14
+
15
+ export const AutofocusContainer = ({
16
+ children,
17
+ wrapChildrenInAccessibleView = true,
18
+ ...viewProps
19
+ }: AutofocusContainerProps) => {
20
+ const containerRef = React.useRef(null);
21
+ const { setFocus } = useFocus();
22
+
23
+ React.useEffect(() => {
24
+ setTimeout(() => {
25
+ setFocus(containerRef.current);
26
+ }, 0);
27
+ }, [setFocus]);
28
+
29
+ return wrapChildrenInAccessibleView ? (
30
+ <TouchableWithoutFeedback ref={containerRef}>
31
+ <View
32
+ accessible={true}
33
+ testID="autofocusContainer.accessibleView"
34
+ {...viewProps}>
35
+ {children}
36
+ </View>
37
+ </TouchableWithoutFeedback>
38
+ ) : (
39
+ <TouchableWithoutFeedback ref={containerRef}>
40
+ {children}
41
+ </TouchableWithoutFeedback>
42
+ );
43
+ };
@@ -0,0 +1,6 @@
1
+ import { PropsWithChildren } from 'react';
2
+ type HideChildrenFromAccessibilityTreeProps = {
3
+ testID?: string;
4
+ };
5
+ export declare const HideChildrenFromAccessibilityTree: (props: PropsWithChildren<HideChildrenFromAccessibilityTreeProps>) => JSX.Element;
6
+ export {};
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.HideChildrenFromAccessibilityTree = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const react_native_1 = require("react-native");
9
+ const AMAProvider_1 = require("./AMAProvider");
10
+ const HideChildrenFromAccessibilityTree = (props) => {
11
+ const { isScreenReaderEnabled } = (0, AMAProvider_1.useAMAContext)();
12
+ if (!isScreenReaderEnabled) {
13
+ return <>{props.children}</>;
14
+ }
15
+ return react_native_1.Platform.select({
16
+ default: <HideChildrenFromAccessibilityAndroid {...props}/>,
17
+ ios: <HideChildrenFromAccessibilityTreeIOS {...props}/>,
18
+ });
19
+ };
20
+ exports.HideChildrenFromAccessibilityTree = HideChildrenFromAccessibilityTree;
21
+ const HideChildrenFromAccessibilityAndroid = ({ children, testID, }) => {
22
+ return (<react_native_1.View importantForAccessibility="no-hide-descendants" testID={testID}>
23
+ {children}
24
+ </react_native_1.View>);
25
+ };
26
+ const HideChildrenFromAccessibilityTreeIOS = ({ children, }) => {
27
+ return hideChildrenFromAccessibilityTree(children);
28
+ };
29
+ const hideChildrenFromAccessibilityTree = (component) => {
30
+ return (react_1.default.Children.map(component, child => {
31
+ return react_1.default.isValidElement(child)
32
+ ? react_1.default.cloneElement(child, {
33
+ // @ts-ignore
34
+ importantForAccessibility: 'no',
35
+ accessibilityElementsHidden: true,
36
+ children: hideChildrenFromAccessibilityTree(child.props.children),
37
+ })
38
+ : child;
39
+ }) || null);
40
+ };
@@ -0,0 +1,129 @@
1
+ /* eslint-disable jest/no-disabled-tests */
2
+ import { act, render } from '@testing-library/react-native';
3
+ import { flushMicroTasks } from '@testing-library/react-native/build/flush-micro-tasks';
4
+ import * as React from 'react';
5
+ import { Pressable, Text } from 'react-native';
6
+
7
+ import { AMAProvider } from './AMAProvider';
8
+ import { HideChildrenFromAccessibilityTree } from './HideChildrenFromAccessibilityTree';
9
+
10
+ beforeEach(() => {
11
+ jest.clearAllMocks();
12
+ });
13
+
14
+ describe.skip('HideChildrenFromAccessibilityTree', () => {
15
+ describe('iOS', () => {
16
+ beforeEach(() => {
17
+ const Platform = jest.requireActual(
18
+ 'react-native/Libraries/Utilities/Platform',
19
+ );
20
+
21
+ Platform.select = (params: any) => {
22
+ return params.ios;
23
+ };
24
+ });
25
+
26
+ it('hides all the children from the accessibility tree when the screen reader is enabled', async () => {
27
+ const { getByTestId } = await renderHideChildrenFromAccessibilityTree();
28
+
29
+ ['test-text', 'test-button', 'nested-text'].forEach(testID => {
30
+ expect(getByTestId(testID).props).toEqual(
31
+ expect.objectContaining({
32
+ importantForAccessibility: 'no',
33
+ accessibilityElementsHidden: true,
34
+ }),
35
+ );
36
+ });
37
+ });
38
+
39
+ it('does nothing if the SR is turned off %s', async () => {
40
+ const { getByTestId } = await renderHideChildrenFromAccessibilityTree({
41
+ isScreenReaderEnabled: false,
42
+ });
43
+
44
+ ['test-text', 'test-button', 'nested-text'].forEach(testID => {
45
+ expect(getByTestId(testID).props).not.toHaveProperty(
46
+ 'importantForAccessibility',
47
+ );
48
+
49
+ expect(getByTestId(testID).props).not.toHaveProperty(
50
+ 'accessibilityElementsHidden',
51
+ );
52
+ });
53
+ });
54
+ });
55
+
56
+ describe('Android', () => {
57
+ beforeEach(() => {
58
+ const Platform = jest.requireActual(
59
+ 'react-native/Libraries/Utilities/Platform',
60
+ );
61
+
62
+ Platform.select = (params: any) => {
63
+ return params.default;
64
+ };
65
+ });
66
+
67
+ it('hides all the children from the accessibility tree when the screen reader is enabled', async () => {
68
+ const { getByTestId } = await renderHideChildrenFromAccessibilityTree();
69
+
70
+ expect(getByTestId('test-wrapper').props).toEqual(
71
+ expect.objectContaining({
72
+ importantForAccessibility: 'no-hide-descendants',
73
+ }),
74
+ );
75
+ });
76
+
77
+ it('does nothing if the SR is turned off %s', async () => {
78
+ const { queryByTestId } = await renderHideChildrenFromAccessibilityTree({
79
+ isScreenReaderEnabled: false,
80
+ });
81
+
82
+ expect(queryByTestId('test-wrapper')).toBeNull();
83
+ });
84
+ });
85
+ });
86
+
87
+ const renderHideChildrenFromAccessibilityTree = async (params?: {
88
+ isScreenReaderEnabled: boolean;
89
+ }) => {
90
+ useAMAContext.mockReturnValue({
91
+ isScreenReaderEnabled: params?.isScreenReaderEnabled ?? true,
92
+ });
93
+
94
+ const renderAPI = render(
95
+ <AMAProvider>
96
+ <HideChildrenFromAccessibilityTree testID="test-wrapper">
97
+ <Text testID="test-text">Test here</Text>
98
+ <>
99
+ <Pressable testID="test-button">
100
+ <Text testID="nested-text">Press me</Text>
101
+ </Pressable>
102
+ </>
103
+ </HideChildrenFromAccessibilityTree>
104
+ </AMAProvider>,
105
+ );
106
+
107
+ await act(async () => {
108
+ await flushMicroTasks();
109
+ });
110
+
111
+ return renderAPI;
112
+ };
113
+
114
+ let useAMAContext: jest.Mock;
115
+
116
+ function mockAMAProvider() {
117
+ const originalModule = jest.requireActual('./AMAProvider');
118
+
119
+ useAMAContext = jest.fn().mockReturnValue({
120
+ isScreenReaderEnabled: true,
121
+ });
122
+
123
+ return {
124
+ ...originalModule,
125
+ useAMAContext,
126
+ };
127
+ }
128
+
129
+ jest.mock('./AMAProvider', () => mockAMAProvider());
@@ -0,0 +1,55 @@
1
+ import React, { PropsWithChildren } from 'react';
2
+ import { Platform, View } from 'react-native';
3
+
4
+ import { useAMAContext } from './AMAProvider';
5
+
6
+ type HideChildrenFromAccessibilityTreeProps = {
7
+ testID?: string;
8
+ };
9
+
10
+ export const HideChildrenFromAccessibilityTree = (
11
+ props: PropsWithChildren<HideChildrenFromAccessibilityTreeProps>,
12
+ ): JSX.Element => {
13
+ const { isScreenReaderEnabled } = useAMAContext();
14
+
15
+ if (!isScreenReaderEnabled) {
16
+ return <>{props.children}</>;
17
+ }
18
+
19
+ return Platform.select({
20
+ default: <HideChildrenFromAccessibilityAndroid {...props} />,
21
+ ios: <HideChildrenFromAccessibilityTreeIOS {...props} />,
22
+ });
23
+ };
24
+
25
+ const HideChildrenFromAccessibilityAndroid = ({
26
+ children,
27
+ testID,
28
+ }: PropsWithChildren<HideChildrenFromAccessibilityTreeProps>) => {
29
+ return (
30
+ <View importantForAccessibility="no-hide-descendants" testID={testID}>
31
+ {children}
32
+ </View>
33
+ );
34
+ };
35
+
36
+ const HideChildrenFromAccessibilityTreeIOS = ({
37
+ children,
38
+ }: PropsWithChildren<{}>) => {
39
+ return hideChildrenFromAccessibilityTree(children);
40
+ };
41
+
42
+ const hideChildrenFromAccessibilityTree = (component: React.ReactNode): any => {
43
+ return (
44
+ React.Children.map(component, child => {
45
+ return React.isValidElement(child)
46
+ ? React.cloneElement(child, {
47
+ // @ts-ignore
48
+ importantForAccessibility: 'no',
49
+ accessibilityElementsHidden: true,
50
+ children: hideChildrenFromAccessibilityTree(child.props.children),
51
+ })
52
+ : child;
53
+ }) || null
54
+ );
55
+ };
@@ -0,0 +1,8 @@
1
+ import type React from 'react';
2
+ import type { PressableStateCallbackType } from 'react-native';
3
+ export declare const useButtonChecks: ((props: Record<string, any>, children?: React.ReactNode | ((state: PressableStateCallbackType) => React.ReactNode), shouldPerformContrastCheck?: boolean) => {
4
+ onLayout: (event: import("react-native").LayoutChangeEvent) => void;
5
+ debugStyle: any[] | ((...params: any) => any) | {
6
+ [x: string]: any;
7
+ } | undefined;
8
+ } | null) | null;