@react-native-ama/core 1.2.1 → 2.0.0-beta.1
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.
- package/dist/ReactNativeAma.types.d.ts +66 -0
- package/dist/ReactNativeAma.types.js +2 -0
- package/dist/ReactNativeAmaModule.d.ts +11 -0
- package/dist/ReactNativeAmaModule.js +5 -0
- package/dist/ReactNativeAmaModule.web.d.ts +9 -0
- package/dist/ReactNativeAmaModule.web.js +27 -0
- package/dist/ReactNativeAmaView.d.ts +3 -0
- package/dist/ReactNativeAmaView.js +32 -0
- package/dist/ReactNativeAmaView.web.d.ts +3 -0
- package/dist/ReactNativeAmaView.web.js +34 -0
- package/dist/{src/components → components}/AMAProvider.d.ts +4 -2
- package/dist/{src/components → components}/AMAProvider.js +19 -45
- package/dist/{src/components → components}/AutofocusContainer.d.ts +2 -1
- package/dist/{src/components → components}/HideChildrenFromAccessibilityTree.d.ts +1 -1
- package/dist/{src/components → components}/HideChildrenFromAccessibilityTree.js +7 -5
- package/dist/{src/hooks → hooks}/useFocus.js +10 -3
- package/dist/{src/index.d.ts → index.d.ts} +3 -2
- package/dist/index.js +38 -0
- package/dist/internals/checks/checkAriaLabel.d.ts +3 -0
- package/dist/internals/checks/checkAriaLabel.js +15 -0
- package/dist/internals/checks/checkAriaRole.d.ts +3 -0
- package/dist/internals/checks/checkAriaRole.js +48 -0
- package/dist/internals/checks/checkContrast.d.ts +3 -0
- package/dist/internals/checks/checkContrast.js +84 -0
- package/dist/internals/checks/checkImageAlt.d.ts +3 -0
- package/dist/internals/checks/checkImageAlt.js +20 -0
- package/dist/internals/checks/checkIsUppercase.d.ts +8 -0
- package/dist/internals/checks/checkIsUppercase.js +30 -0
- package/dist/internals/checks/checkLongNumber.d.ts +3 -0
- package/dist/internals/checks/checkLongNumber.js +31 -0
- package/dist/internals/checks/checkMinimumSize.d.ts +3 -0
- package/dist/internals/checks/checkMinimumSize.js +22 -0
- package/dist/internals/checks/checkTextInput.d.ts +3 -0
- package/dist/internals/checks/checkTextInput.js +51 -0
- package/dist/internals/checks/performChecks.d.ts +3 -0
- package/dist/internals/checks/performChecks.js +23 -0
- package/dist/internals/components/AMAErrorOverlay.d.ts +5 -0
- package/dist/internals/components/AMAErrorOverlay.js +334 -0
- package/dist/internals/components/AMARuleError.d.ts +7 -0
- package/dist/internals/components/AMARuleError.js +161 -0
- package/dist/internals/config.d.ts +23 -0
- package/dist/internals/config.js +33 -0
- package/dist/internals/types.d.ts +9 -0
- package/dist/internals/types.js +2 -0
- package/dist/internals/useAMADev.d.ts +8 -0
- package/dist/internals/useAMADev.dev.d.ts +2 -0
- package/dist/internals/useAMADev.dev.js +110 -0
- package/dist/internals/useAMADev.js +6 -0
- package/dist/internals/useAMADevNodeChecks.dev.d.ts +16 -0
- package/dist/internals/useAMADevNodeChecks.dev.js +115 -0
- package/dist/internals/useAMADevUiInteraction.dev.d.ts +7 -0
- package/dist/internals/useAMADevUiInteraction.dev.js +100 -0
- package/dist/internals/utils/amaClearHighlight.d.ts +2 -0
- package/dist/internals/utils/amaClearHighlight.js +12 -0
- package/dist/internals/utils/amaHighlightComponent.d.ts +3 -0
- package/dist/internals/utils/amaHighlightComponent.js +51 -0
- package/dist/internals/utils/constants.d.ts +3 -0
- package/dist/internals/utils/constants.js +32 -0
- package/dist/internals/utils/getErrorColor.d.ts +2 -0
- package/dist/internals/utils/getErrorColor.js +10 -0
- package/dist/internals/utils/getRuleAction.d.ts +2 -0
- package/dist/internals/utils/getRuleAction.js +40 -0
- package/dist/internals/utils/getRuleErrorInfo.d.ts +7 -0
- package/dist/internals/utils/getRuleErrorInfo.js +19 -0
- package/dist/internals/utils/ignoreContrastCheck.d.ts +1 -0
- package/dist/internals/utils/ignoreContrastCheck.js +15 -0
- package/dist/internals/utils/interpolateAnimationStates.d.ts +2 -0
- package/dist/internals/utils/interpolateAnimationStates.js +43 -0
- package/dist/internals/utils/isA11yLabelAllowed.d.ts +1 -0
- package/dist/internals/utils/isA11yLabelAllowed.js +14 -0
- package/dist/internals/utils/isRuleDisabled.d.ts +2 -0
- package/dist/internals/utils/isRuleDisabled.js +10 -0
- package/dist/internals/utils/logError.d.ts +2 -0
- package/dist/internals/utils/logError.js +36 -0
- package/dist/internals/utils/logFoundIssues.d.ts +2 -0
- package/dist/internals/utils/logFoundIssues.js +23 -0
- package/dist/internals/utils/logger.d.ts +6 -0
- package/dist/internals/utils/logger.js +15 -0
- package/dist/internals/utils/minimumTouchableSize.d.ts +1 -0
- package/dist/internals/utils/minimumTouchableSize.js +9 -0
- package/dist/internals/utils/platformHelpers.d.ts +2 -0
- package/dist/internals/utils/platformHelpers.js +6 -0
- package/dist/internals/utils/rules.d.ts +22 -0
- package/dist/internals/utils/rules.js +253 -0
- package/dist/scripts/create-config.js +4 -3
- package/package.json +62 -20
- package/src/ReactNativeAma.types.ts +70 -0
- package/src/ReactNativeAmaModule.ts +18 -0
- package/src/ReactNativeAmaModule.web.ts +14 -0
- package/src/ReactNativeAmaView.tsx +9 -0
- package/src/ReactNativeAmaView.web.tsx +15 -0
- package/src/__mocks__/ReactNativeAmaModule.ts +9 -0
- package/src/components/AMAProvider.integration.test.tsx +3 -96
- package/src/components/AMAProvider.test.tsx +9 -4
- package/src/components/AMAProvider.tsx +40 -76
- package/src/components/AutofocusContainer.test.tsx +4 -5
- package/src/components/AutofocusContainer.tsx +20 -5
- package/src/components/HideChildrenFromAccessibilityTree.test.tsx +23 -39
- package/src/components/HideChildrenFromAccessibilityTree.tsx +12 -11
- package/src/hooks/useFocus.test.ts +20 -20
- package/src/hooks/useFocus.ts +14 -6
- package/src/hooks/useTimedAction.android.test.ts +0 -1
- package/src/hooks/useTimedAction.ios.test.ts +0 -1
- package/src/hooks/useTimedAction.ts +0 -1
- package/src/index.ts +4 -2
- package/src/internals/checks/checkAriaLabel.test.ts +165 -0
- package/src/internals/checks/checkAriaLabel.ts +16 -0
- package/src/internals/checks/checkAriaRole.test.ts +429 -0
- package/src/internals/checks/checkAriaRole.ts +60 -0
- package/src/internals/checks/checkContrast.test.ts +278 -0
- package/src/internals/checks/checkContrast.ts +109 -0
- package/src/internals/checks/checkImageAlt.test.ts +125 -0
- package/src/internals/checks/checkImageAlt.ts +23 -0
- package/src/internals/checks/checkIsUppercase.test.ts +399 -0
- package/src/internals/checks/checkIsUppercase.ts +46 -0
- package/src/internals/checks/checkLongNumber.test.ts +111 -0
- package/src/internals/checks/checkLongNumber.ts +34 -0
- package/src/internals/checks/checkMinimumSize.test.ts +67 -0
- package/src/internals/checks/checkMinimumSize.ts +25 -0
- package/src/internals/checks/checkTextInput.ts +65 -0
- package/src/internals/checks/performChecks.test.ts +171 -0
- package/src/internals/checks/performChecks.ts +24 -0
- package/src/internals/components/AMAErrorOverlay.test.tsx +139 -0
- package/src/internals/components/AMAErrorOverlay.tsx +451 -0
- package/src/internals/components/AMARuleError.test.tsx +93 -0
- package/src/internals/components/AMARuleError.tsx +225 -0
- package/src/internals/config.ts +58 -0
- package/src/internals/types.ts +11 -0
- package/src/internals/useAMADev.dev.ts +153 -0
- package/src/internals/useAMADev.test.ts +260 -0
- package/src/internals/useAMADev.ts +17 -0
- package/src/internals/useAMADevNodeChecks.dev.ts +178 -0
- package/src/internals/useAMADevUiInteraction.dev.ts +164 -0
- package/src/internals/utils/amaClearHighlight.test.ts +21 -0
- package/src/internals/utils/amaClearHighlight.ts +8 -0
- package/src/internals/utils/amaHighlightComponent.test.ts +74 -0
- package/src/internals/utils/amaHighlightComponent.ts +46 -0
- package/src/internals/utils/constants.ts +33 -0
- package/src/internals/utils/devMode.test.ts +99 -0
- package/src/internals/utils/getErrorColor.test.ts +18 -0
- package/src/internals/utils/getErrorColor.ts +8 -0
- package/src/internals/utils/getRuleAction.ts +13 -0
- package/src/internals/utils/getRuleErrorInfo.test.ts +59 -0
- package/src/internals/utils/getRuleErrorInfo.ts +26 -0
- package/src/internals/utils/ignoreContrastCheck.test.ts +34 -0
- package/src/internals/utils/ignoreContrastCheck.ts +11 -0
- package/src/internals/utils/interpolateAnimation.test.ts +176 -0
- package/src/internals/utils/interpolateAnimationStates.ts +55 -0
- package/src/internals/utils/isA11yLabelAllowed.test.ts +39 -0
- package/src/internals/utils/isA11yLabelAllowed.ts +10 -0
- package/src/internals/utils/isRuleDisabled.test.ts +35 -0
- package/src/internals/utils/isRuleDisabled.ts +10 -0
- package/src/internals/utils/logError.test.ts +148 -0
- package/src/internals/utils/logError.ts +41 -0
- package/src/internals/utils/logFoundIssues.test.ts +98 -0
- package/src/internals/utils/logFoundIssues.ts +22 -0
- package/src/internals/utils/logger.test.ts +26 -0
- package/src/internals/utils/logger.ts +15 -0
- package/src/internals/utils/loggerMethods.test.ts +38 -0
- package/src/internals/utils/minimumTouchableSize.test.ts +28 -0
- package/src/internals/utils/minimumTouchableSize.tsx +9 -0
- package/src/internals/utils/platformHelpers.test.ts +11 -0
- package/src/internals/utils/platformHelpers.ts +4 -0
- package/src/internals/utils/rules.ts +363 -0
- package/src/scripts/create-config.ts +21 -0
- package/dist/src/hooks/useButtonChecks.d.ts +0 -6
- package/dist/src/hooks/useButtonChecks.js +0 -51
- package/dist/src/hooks/useChecks.d.ts +0 -15
- package/dist/src/hooks/useChecks.js +0 -152
- package/dist/src/index.js +0 -20
- package/src/hooks/useButtonChecks.ts +0 -73
- package/src/hooks/useChecks.test.ts +0 -214
- package/src/hooks/useChecks.ts +0 -207
- /package/dist/{src/components → components}/AutofocusContainer.js +0 -0
- /package/dist/{src/hooks → hooks}/useFocus.d.ts +0 -0
- /package/dist/{src/hooks → hooks}/useTimedAction.d.ts +0 -0
- /package/dist/{src/hooks → hooks}/useTimedAction.js +0 -0
- /package/dist/{src/utils → utils}/numerify.d.ts +0 -0
- /package/dist/{src/utils → utils}/numerify.js +0 -0
|
@@ -2,13 +2,15 @@ import { act, render } from '@testing-library/react-native';
|
|
|
2
2
|
import { flushMicroTasks } from '@testing-library/react-native/build/flush-micro-tasks';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { AccessibilityInfo } from 'react-native';
|
|
5
|
-
|
|
6
5
|
import { AMAContextValue, AMAProvider } from './AMAProvider';
|
|
7
6
|
|
|
8
7
|
var mockProvider: jest.Mock;
|
|
9
8
|
|
|
10
9
|
beforeEach(() => {
|
|
11
10
|
jest.clearAllMocks();
|
|
11
|
+
if (mockProvider) {
|
|
12
|
+
mockProvider.mockReturnValue(null);
|
|
13
|
+
}
|
|
12
14
|
});
|
|
13
15
|
|
|
14
16
|
describe('AMAProvider', () => {
|
|
@@ -103,9 +105,10 @@ describe('AMAProvider', () => {
|
|
|
103
105
|
expect.objectContaining({
|
|
104
106
|
value: expect.objectContaining(state),
|
|
105
107
|
}),
|
|
106
|
-
|
|
108
|
+
undefined,
|
|
107
109
|
);
|
|
108
110
|
|
|
111
|
+
|
|
109
112
|
jest.clearAllMocks();
|
|
110
113
|
|
|
111
114
|
act(() => {
|
|
@@ -117,9 +120,10 @@ describe('AMAProvider', () => {
|
|
|
117
120
|
expect.objectContaining({
|
|
118
121
|
value: expect.objectContaining(state),
|
|
119
122
|
}),
|
|
120
|
-
|
|
123
|
+
undefined,
|
|
121
124
|
);
|
|
122
125
|
|
|
126
|
+
|
|
123
127
|
jest.clearAllMocks();
|
|
124
128
|
|
|
125
129
|
act(() => {
|
|
@@ -131,8 +135,9 @@ describe('AMAProvider', () => {
|
|
|
131
135
|
expect.objectContaining({
|
|
132
136
|
value: expect.objectContaining(state),
|
|
133
137
|
}),
|
|
134
|
-
|
|
138
|
+
undefined,
|
|
135
139
|
);
|
|
140
|
+
|
|
136
141
|
},
|
|
137
142
|
);
|
|
138
143
|
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/* eslint-disable react-native/no-inline-styles */
|
|
2
|
-
|
|
3
|
-
import { RED } from '@react-native-ama/internal';
|
|
4
2
|
import * as React from 'react';
|
|
5
3
|
import {
|
|
6
4
|
AccessibilityChangeEventName,
|
|
7
5
|
AccessibilityInfo,
|
|
8
6
|
NativeEventSubscription,
|
|
9
|
-
Text,
|
|
10
7
|
View,
|
|
11
8
|
} from 'react-native';
|
|
9
|
+
import type { AmaRule } from '../internals/types';
|
|
10
|
+
import type { UseAMADev } from '../internals/useAMADev';
|
|
12
11
|
|
|
13
12
|
type AMAProviderProps = {
|
|
14
13
|
children: React.ReactNode;
|
|
@@ -21,14 +20,16 @@ type SharedContextValue = {
|
|
|
21
20
|
isInvertColorsEnabled: boolean;
|
|
22
21
|
isReduceMotionEnabled: boolean;
|
|
23
22
|
isReduceTransparencyEnabled: boolean;
|
|
23
|
+
isHighTextContrastEnabled: boolean;
|
|
24
|
+
isDarkerSystemColorsEnabled: boolean;
|
|
24
25
|
reactNavigationScreenOptions: {
|
|
25
26
|
animationEnabled: boolean;
|
|
26
27
|
animation: 'default' | 'fade';
|
|
27
28
|
};
|
|
28
29
|
};
|
|
30
|
+
|
|
29
31
|
export type AMADevContextValue = SharedContextValue & {
|
|
30
|
-
trackError: (
|
|
31
|
-
removeError: (id: string) => void;
|
|
32
|
+
trackError: (rule: AmaRule, ref?: React.RefObject<any>) => void;
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
export type AMAProdContextValue = SharedContextValue & {};
|
|
@@ -39,7 +40,7 @@ type AccessibilityEvents = Exclude<AccessibilityChangeEventName, 'change'>;
|
|
|
39
40
|
|
|
40
41
|
type AccessibilityInfoKey = Exclude<
|
|
41
42
|
keyof AMAContextValue,
|
|
42
|
-
'reactNavigationScreenOptions' | 'trackError'
|
|
43
|
+
'reactNavigationScreenOptions' | 'trackError'
|
|
43
44
|
>;
|
|
44
45
|
|
|
45
46
|
type Extractor<S extends AccessibilityEvents> = S extends `${infer R}Changed`
|
|
@@ -57,10 +58,12 @@ const eventsMapping: AccessibilityInfoEvents = {
|
|
|
57
58
|
boldTextChanged: 'isBoldTextEnabled',
|
|
58
59
|
invertColorsChanged: 'isInvertColorsEnabled',
|
|
59
60
|
screenReaderChanged: 'isScreenReaderEnabled',
|
|
61
|
+
highTextContrastChanged: 'isHighTextContrastEnabled',
|
|
62
|
+
darkerSystemColorsChanged: 'isDarkerSystemColorsEnabled',
|
|
60
63
|
};
|
|
61
64
|
|
|
62
65
|
export const isDevContextValue = (
|
|
63
|
-
value: AMAContextValue
|
|
66
|
+
value: AMAContextValue
|
|
64
67
|
): value is AMADevContextValue =>
|
|
65
68
|
(value as AMADevContextValue).trackError !== undefined;
|
|
66
69
|
|
|
@@ -71,6 +74,8 @@ const DEFAULT_VALUES = {
|
|
|
71
74
|
isInvertColorsEnabled: false,
|
|
72
75
|
isReduceMotionEnabled: false,
|
|
73
76
|
isScreenReaderEnabled: false,
|
|
77
|
+
isHighTextContrastEnabled: false,
|
|
78
|
+
isDarkerSystemColorsEnabled: false,
|
|
74
79
|
reactNavigationScreenOptions: {
|
|
75
80
|
animationEnabled: true,
|
|
76
81
|
animation: 'default',
|
|
@@ -79,12 +84,28 @@ const DEFAULT_VALUES = {
|
|
|
79
84
|
|
|
80
85
|
const AMAContext = React.createContext<AMAContextValue | null>(null);
|
|
81
86
|
|
|
87
|
+
type AMAErrorOverlayModule = typeof import('../internals/components/AMAErrorOverlay');
|
|
88
|
+
type UseAMADevModule = {
|
|
89
|
+
useAMADev: UseAMADev | null;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const AMAErrorOverlay = __DEV__
|
|
93
|
+
? (require('../internals/components/AMAErrorOverlay') as AMAErrorOverlayModule)
|
|
94
|
+
.AMAErrorOverlay
|
|
95
|
+
: null;
|
|
96
|
+
|
|
97
|
+
const useAMADev = __DEV__
|
|
98
|
+
? (require('../internals/useAMADev') as UseAMADevModule).useAMADev
|
|
99
|
+
: null;
|
|
100
|
+
|
|
82
101
|
export const AMAProvider: React.FC<AMAProviderProps> = ({ children }) => {
|
|
83
102
|
const [values, setValues] = React.useState<AMAContextValue>(DEFAULT_VALUES);
|
|
84
103
|
|
|
104
|
+
const { issues, trackError } = (__DEV__ && useAMADev?.()) || {};
|
|
105
|
+
|
|
85
106
|
const handleAccessibilityInfoChanged = (key: AccessibilityInfoKey) => {
|
|
86
107
|
return (newValue: boolean) => {
|
|
87
|
-
setValues(oldValues => {
|
|
108
|
+
setValues((oldValues) => {
|
|
88
109
|
const newValues = { ...oldValues };
|
|
89
110
|
newValues[key] = newValue;
|
|
90
111
|
|
|
@@ -107,27 +128,27 @@ export const AMAProvider: React.FC<AMAProviderProps> = ({ children }) => {
|
|
|
107
128
|
const allInitPromises: Promise<boolean>[] = [];
|
|
108
129
|
|
|
109
130
|
const subscriptions: NativeEventSubscription[] = Object.entries(
|
|
110
|
-
eventsMapping
|
|
131
|
+
eventsMapping
|
|
111
132
|
).map(([eventName, contextKey]) => {
|
|
112
133
|
allInitPromises.push(AccessibilityInfo[contextKey]());
|
|
113
134
|
|
|
114
135
|
return AccessibilityInfo.addEventListener(
|
|
115
136
|
eventName as AccessibilityEvents,
|
|
116
|
-
handleAccessibilityInfoChanged(contextKey)
|
|
137
|
+
handleAccessibilityInfoChanged(contextKey)
|
|
117
138
|
);
|
|
118
139
|
});
|
|
119
140
|
|
|
120
|
-
Promise.all(allInitPromises).then(promisesValues => {
|
|
141
|
+
Promise.all(allInitPromises).then((promisesValues) => {
|
|
121
142
|
const newValues = Object.values(eventsMapping).reduce(
|
|
122
143
|
(list, key, index) => {
|
|
123
144
|
list[key] = promisesValues[index] as boolean;
|
|
124
145
|
|
|
125
146
|
return list;
|
|
126
147
|
},
|
|
127
|
-
{} as AMAContextValue
|
|
148
|
+
{} as AMAContextValue
|
|
128
149
|
);
|
|
129
150
|
|
|
130
|
-
setValues(oldValues => {
|
|
151
|
+
setValues((oldValues) => {
|
|
131
152
|
return {
|
|
132
153
|
...oldValues,
|
|
133
154
|
...newValues,
|
|
@@ -136,52 +157,22 @@ export const AMAProvider: React.FC<AMAProviderProps> = ({ children }) => {
|
|
|
136
157
|
});
|
|
137
158
|
|
|
138
159
|
return () => {
|
|
139
|
-
subscriptions.forEach(subscription => subscription?.remove());
|
|
160
|
+
subscriptions.forEach((subscription) => subscription?.remove());
|
|
140
161
|
};
|
|
141
162
|
}, []);
|
|
142
163
|
|
|
143
|
-
const [failedItems, setFailedItems] = React.useState<string[]>([]);
|
|
144
|
-
|
|
145
164
|
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
165
|
return (
|
|
173
166
|
<AMAContext.Provider
|
|
174
167
|
value={{
|
|
175
168
|
...values,
|
|
176
|
-
trackError,
|
|
177
|
-
|
|
178
|
-
|
|
169
|
+
trackError: trackError || (() => { }),
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
179
172
|
<View style={{ flex: 1 }}>
|
|
180
173
|
<>
|
|
181
174
|
{children}
|
|
182
|
-
{
|
|
183
|
-
<AMAError count={failedItems.length} />
|
|
184
|
-
) : null}
|
|
175
|
+
{AMAErrorOverlay && <AMAErrorOverlay issues={issues} />}
|
|
185
176
|
</>
|
|
186
177
|
</View>
|
|
187
178
|
</AMAContext.Provider>
|
|
@@ -191,36 +182,9 @@ export const AMAProvider: React.FC<AMAProviderProps> = ({ children }) => {
|
|
|
191
182
|
return <AMAContext.Provider value={values}>{children}</AMAContext.Provider>;
|
|
192
183
|
};
|
|
193
184
|
|
|
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
185
|
export const useAMAContext = () => {
|
|
223
186
|
const context = React.useContext(AMAContext);
|
|
187
|
+
|
|
224
188
|
if (!context) {
|
|
225
189
|
throw new Error('Please wrap your app with <AMAProvider />');
|
|
226
190
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { render, waitFor } from '@testing-library/react-native';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
|
|
4
3
|
import * as UseFocus from '../hooks/useFocus';
|
|
5
4
|
import { AutofocusContainer } from './AutofocusContainer';
|
|
6
5
|
|
|
@@ -9,7 +8,7 @@ beforeEach(() => {
|
|
|
9
8
|
});
|
|
10
9
|
|
|
11
10
|
describe('AutofocusContainer', () => {
|
|
12
|
-
it('
|
|
11
|
+
it('call setFocus when gets rendered', async () => {
|
|
13
12
|
const setFocus = jest.fn();
|
|
14
13
|
jest.spyOn(UseFocus, 'useFocus').mockReturnValue({
|
|
15
14
|
setFocus,
|
|
@@ -21,7 +20,7 @@ describe('AutofocusContainer', () => {
|
|
|
21
20
|
</AutofocusContainer>,
|
|
22
21
|
);
|
|
23
22
|
|
|
24
|
-
await waitFor(() => expect(setFocus).
|
|
23
|
+
await waitFor(() => expect(setFocus).toHaveBeenCalled());
|
|
25
24
|
});
|
|
26
25
|
|
|
27
26
|
it.each([undefined, true])(
|
|
@@ -38,7 +37,7 @@ describe('AutofocusContainer', () => {
|
|
|
38
37
|
</AutofocusContainer>,
|
|
39
38
|
);
|
|
40
39
|
|
|
41
|
-
await waitFor(() => expect(setFocus).
|
|
40
|
+
await waitFor(() => expect(setFocus).toHaveBeenCalled());
|
|
42
41
|
|
|
43
42
|
expect(
|
|
44
43
|
renderAPI.getByTestId('autofocusContainer.accessibleView'),
|
|
@@ -58,7 +57,7 @@ describe('AutofocusContainer', () => {
|
|
|
58
57
|
</AutofocusContainer>,
|
|
59
58
|
);
|
|
60
59
|
|
|
61
|
-
await waitFor(() => expect(setFocus).
|
|
60
|
+
await waitFor(() => expect(setFocus).toHaveBeenCalled());
|
|
62
61
|
|
|
63
62
|
expect(
|
|
64
63
|
renderAPI.queryByTestId('autofocusContainer.accessibleView'),
|
|
@@ -5,9 +5,21 @@ import {
|
|
|
5
5
|
View,
|
|
6
6
|
ViewProps,
|
|
7
7
|
} from 'react-native';
|
|
8
|
-
|
|
9
8
|
import { useFocus } from '../hooks/useFocus';
|
|
10
|
-
|
|
9
|
+
|
|
10
|
+
type AccessibilityPropKeys =
|
|
11
|
+
| 'accessibilityActions'
|
|
12
|
+
| 'accessibilityElementsHidden'
|
|
13
|
+
| 'accessibilityHint'
|
|
14
|
+
| 'accessibilityLabel'
|
|
15
|
+
| 'accessibilityLiveRegion'
|
|
16
|
+
| 'accessibilityRole'
|
|
17
|
+
| 'accessibilityState'
|
|
18
|
+
| 'accessibilityViewIsModal'
|
|
19
|
+
| 'onAccessibilityAction'
|
|
20
|
+
| 'testID';
|
|
21
|
+
|
|
22
|
+
type PickAccessibleProps<T> = Pick<T, Extract<keyof T, AccessibilityPropKeys>>;
|
|
11
23
|
|
|
12
24
|
type TouchableAccessibleProps =
|
|
13
25
|
PickAccessibleProps<TouchableWithoutFeedbackProps>;
|
|
@@ -46,18 +58,21 @@ export const AutofocusContainer = ({
|
|
|
46
58
|
return wrapChildrenInAccessibleView ? (
|
|
47
59
|
<TouchableWithoutFeedback
|
|
48
60
|
ref={containerRef}
|
|
49
|
-
{...touchableContainerAccessibilityProps}
|
|
61
|
+
{...touchableContainerAccessibilityProps}
|
|
62
|
+
>
|
|
50
63
|
<View
|
|
51
64
|
accessible={true}
|
|
52
65
|
testID="autofocusContainer.accessibleView"
|
|
53
|
-
{...viewProps}
|
|
66
|
+
{...viewProps}
|
|
67
|
+
>
|
|
54
68
|
{children}
|
|
55
69
|
</View>
|
|
56
70
|
</TouchableWithoutFeedback>
|
|
57
71
|
) : (
|
|
58
72
|
<TouchableWithoutFeedback
|
|
59
73
|
ref={containerRef}
|
|
60
|
-
{...touchableContainerAccessibilityProps}
|
|
74
|
+
{...touchableContainerAccessibilityProps}
|
|
75
|
+
>
|
|
61
76
|
{children}
|
|
62
77
|
</TouchableWithoutFeedback>
|
|
63
78
|
);
|
|
@@ -1,33 +1,39 @@
|
|
|
1
|
-
/* eslint-disable jest/no-disabled-tests */
|
|
2
1
|
import { act, render } from '@testing-library/react-native';
|
|
3
2
|
import { flushMicroTasks } from '@testing-library/react-native/build/flush-micro-tasks';
|
|
4
3
|
import * as React from 'react';
|
|
5
4
|
import { Pressable, Text } from 'react-native';
|
|
6
|
-
|
|
7
5
|
import { AMAProvider } from './AMAProvider';
|
|
8
6
|
import { HideChildrenFromAccessibilityTree } from './HideChildrenFromAccessibilityTree';
|
|
9
7
|
|
|
8
|
+
jest.mock('./AMAProvider', () => {
|
|
9
|
+
const originalModule = jest.requireActual('./AMAProvider');
|
|
10
|
+
return {
|
|
11
|
+
...originalModule,
|
|
12
|
+
useAMAContext: jest.fn().mockReturnValue({ isScreenReaderEnabled: true }),
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
let useAMAContext: jest.Mock;
|
|
17
|
+
|
|
10
18
|
beforeEach(() => {
|
|
11
19
|
jest.clearAllMocks();
|
|
20
|
+
useAMAContext = jest.requireMock('./AMAProvider').useAMAContext;
|
|
21
|
+
useAMAContext.mockReturnValue({ isScreenReaderEnabled: true });
|
|
12
22
|
});
|
|
13
23
|
|
|
14
|
-
describe
|
|
24
|
+
describe('HideChildrenFromAccessibilityTree', () => {
|
|
15
25
|
describe('iOS', () => {
|
|
16
26
|
beforeEach(() => {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
Platform.select = (params: any) => {
|
|
22
|
-
return params.ios;
|
|
23
|
-
};
|
|
27
|
+
const rn = jest.requireMock('react-native');
|
|
28
|
+
rn.Platform.OS = 'ios';
|
|
29
|
+
rn.Platform.select = (params: any) => params.ios ?? params.default;
|
|
24
30
|
});
|
|
25
31
|
|
|
26
32
|
it('hides all the children from the accessibility tree when the screen reader is enabled', async () => {
|
|
27
33
|
const { getByTestId } = await renderHideChildrenFromAccessibilityTree();
|
|
28
34
|
|
|
29
35
|
['test-text', 'test-button', 'nested-text'].forEach(testID => {
|
|
30
|
-
expect(getByTestId(testID).props).toEqual(
|
|
36
|
+
expect(getByTestId(testID, { includeHiddenElements: true }).props).toEqual(
|
|
31
37
|
expect.objectContaining({
|
|
32
38
|
importantForAccessibility: 'no',
|
|
33
39
|
accessibilityElementsHidden: true,
|
|
@@ -36,7 +42,7 @@ describe.skip('HideChildrenFromAccessibilityTree', () => {
|
|
|
36
42
|
});
|
|
37
43
|
});
|
|
38
44
|
|
|
39
|
-
it('does nothing if the SR is turned off
|
|
45
|
+
it('does nothing if the SR is turned off', async () => {
|
|
40
46
|
const { getByTestId } = await renderHideChildrenFromAccessibilityTree({
|
|
41
47
|
isScreenReaderEnabled: false,
|
|
42
48
|
});
|
|
@@ -45,7 +51,6 @@ describe.skip('HideChildrenFromAccessibilityTree', () => {
|
|
|
45
51
|
expect(getByTestId(testID).props).not.toHaveProperty(
|
|
46
52
|
'importantForAccessibility',
|
|
47
53
|
);
|
|
48
|
-
|
|
49
54
|
expect(getByTestId(testID).props).not.toHaveProperty(
|
|
50
55
|
'accessibilityElementsHidden',
|
|
51
56
|
);
|
|
@@ -55,26 +60,22 @@ describe.skip('HideChildrenFromAccessibilityTree', () => {
|
|
|
55
60
|
|
|
56
61
|
describe('Android', () => {
|
|
57
62
|
beforeEach(() => {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
Platform.select = (params: any) => {
|
|
63
|
-
return params.default;
|
|
64
|
-
};
|
|
63
|
+
const rn = jest.requireMock('react-native');
|
|
64
|
+
rn.Platform.OS = 'android';
|
|
65
|
+
rn.Platform.select = (params: any) => params.default ?? params.android;
|
|
65
66
|
});
|
|
66
67
|
|
|
67
68
|
it('hides all the children from the accessibility tree when the screen reader is enabled', async () => {
|
|
68
69
|
const { getByTestId } = await renderHideChildrenFromAccessibilityTree();
|
|
69
70
|
|
|
70
|
-
expect(getByTestId('test-wrapper').props).toEqual(
|
|
71
|
+
expect(getByTestId('test-wrapper', { includeHiddenElements: true }).props).toEqual(
|
|
71
72
|
expect.objectContaining({
|
|
72
73
|
importantForAccessibility: 'no-hide-descendants',
|
|
73
74
|
}),
|
|
74
75
|
);
|
|
75
76
|
});
|
|
76
77
|
|
|
77
|
-
it('does nothing if the SR is turned off
|
|
78
|
+
it('does nothing if the SR is turned off', async () => {
|
|
78
79
|
const { queryByTestId } = await renderHideChildrenFromAccessibilityTree({
|
|
79
80
|
isScreenReaderEnabled: false,
|
|
80
81
|
});
|
|
@@ -110,20 +111,3 @@ const renderHideChildrenFromAccessibilityTree = async (params?: {
|
|
|
110
111
|
|
|
111
112
|
return renderAPI;
|
|
112
113
|
};
|
|
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());
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import React, { PropsWithChildren } from 'react';
|
|
1
|
+
import React, { PropsWithChildren, type JSX } from 'react';
|
|
2
2
|
import { Platform, View } from 'react-native';
|
|
3
|
-
|
|
4
3
|
import { useAMAContext } from './AMAProvider';
|
|
5
4
|
|
|
6
5
|
type HideChildrenFromAccessibilityTreeProps = {
|
|
@@ -39,17 +38,19 @@ const HideChildrenFromAccessibilityTreeIOS = ({
|
|
|
39
38
|
return hideChildrenFromAccessibilityTree(children);
|
|
40
39
|
};
|
|
41
40
|
|
|
42
|
-
const hideChildrenFromAccessibilityTree = (component: React.ReactNode):
|
|
41
|
+
const hideChildrenFromAccessibilityTree = (component: React.ReactNode): React.ReactNode => {
|
|
43
42
|
return (
|
|
44
43
|
React.Children.map(component, child => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
if (React.isValidElement(child)) {
|
|
45
|
+
const element = child as React.ReactElement<{ children?: React.ReactNode }>;
|
|
46
|
+
return React.cloneElement(element, {
|
|
47
|
+
// @ts-ignore
|
|
48
|
+
importantForAccessibility: 'no',
|
|
49
|
+
accessibilityElementsHidden: true,
|
|
50
|
+
children: hideChildrenFromAccessibilityTree(element.props.children),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return child;
|
|
53
54
|
}) || null
|
|
54
55
|
);
|
|
55
56
|
};
|
|
@@ -1,16 +1,29 @@
|
|
|
1
|
-
import { SHELL_COLORS } from '@react-native-ama/internal';
|
|
2
1
|
import { renderHook } from '@testing-library/react-native';
|
|
3
|
-
|
|
4
2
|
import { useFocus } from './useFocus';
|
|
5
3
|
|
|
4
|
+
const SHELL_COLORS = {
|
|
5
|
+
BG_RED: '\x1b[41m',
|
|
6
|
+
RESET: '\x1b[0m',
|
|
7
|
+
BLUE: '\x1b[34m',
|
|
8
|
+
YELLOW: '\x1b[33m',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
let findNodeHandleMock: jest.Mock;
|
|
12
|
+
let setAccessibilityFocusMock: jest.Mock;
|
|
13
|
+
|
|
6
14
|
beforeEach(() => {
|
|
15
|
+
jest.useFakeTimers();
|
|
7
16
|
jest.clearAllMocks();
|
|
8
|
-
|
|
9
17
|
jest.spyOn(console, 'warn').mockImplementation();
|
|
18
|
+
|
|
19
|
+
const rn = jest.requireMock('react-native');
|
|
20
|
+
findNodeHandleMock = rn.findNodeHandle;
|
|
21
|
+
setAccessibilityFocusMock = rn.AccessibilityInfo.setAccessibilityFocus;
|
|
10
22
|
});
|
|
11
23
|
|
|
12
|
-
|
|
13
|
-
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
jest.useRealTimers();
|
|
26
|
+
});
|
|
14
27
|
|
|
15
28
|
describe('useFocus', () => {
|
|
16
29
|
it.each([null, undefined])('does nothing if the Ref is %s', refElement => {
|
|
@@ -24,6 +37,7 @@ describe('useFocus', () => {
|
|
|
24
37
|
findNodeHandleMock.mockReturnValue('element-id');
|
|
25
38
|
|
|
26
39
|
renderHook(() => useFocus({ current: 'test-element' }));
|
|
40
|
+
jest.runAllTimers();
|
|
27
41
|
|
|
28
42
|
expect(findNodeHandleMock).toHaveBeenCalledWith('test-element');
|
|
29
43
|
expect(setAccessibilityFocusMock).toHaveBeenNthCalledWith(1, 'element-id');
|
|
@@ -70,6 +84,7 @@ describe('useFocus', () => {
|
|
|
70
84
|
const { result } = renderHook(() => useFocus());
|
|
71
85
|
|
|
72
86
|
result.current.setFocus('component' as any);
|
|
87
|
+
jest.runAllTimers();
|
|
73
88
|
|
|
74
89
|
expect(findNodeHandleMock).toHaveBeenCalledWith('component');
|
|
75
90
|
expect(setAccessibilityFocusMock).toHaveBeenNthCalledWith(
|
|
@@ -82,18 +97,3 @@ describe('useFocus', () => {
|
|
|
82
97
|
);
|
|
83
98
|
});
|
|
84
99
|
});
|
|
85
|
-
|
|
86
|
-
function mockReactNative() {
|
|
87
|
-
findNodeHandleMock = jest.fn();
|
|
88
|
-
setAccessibilityFocusMock = jest.fn();
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
findNodeHandle: findNodeHandleMock,
|
|
92
|
-
AccessibilityInfo: {
|
|
93
|
-
setAccessibilityFocus: setAccessibilityFocusMock,
|
|
94
|
-
},
|
|
95
|
-
Platform: {},
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
jest.mock('react-native', () => mockReactNative());
|
package/src/hooks/useFocus.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import { SHELL_COLORS } from '@react-native-ama/internal';
|
|
2
1
|
import * as React from 'react';
|
|
3
2
|
import {
|
|
4
3
|
AccessibilityInfo,
|
|
5
|
-
InteractionManager,
|
|
6
4
|
findNodeHandle,
|
|
5
|
+
InteractionManager,
|
|
7
6
|
} from 'react-native';
|
|
8
7
|
|
|
8
|
+
const SHELL_COLORS = __DEV__
|
|
9
|
+
? {
|
|
10
|
+
BG_RED: '\x1b[41m',
|
|
11
|
+
RESET: '\x1b[0m',
|
|
12
|
+
BLUE: '\x1b[34m',
|
|
13
|
+
YELLOW: '\x1b[33m',
|
|
14
|
+
}
|
|
15
|
+
: null;
|
|
16
|
+
|
|
9
17
|
export const useFocus = (refComponent?: React.RefObject<any>) => {
|
|
10
18
|
const setFocus = React.useCallback(
|
|
11
19
|
(
|
|
@@ -13,7 +21,7 @@ export const useFocus = (refComponent?: React.RefObject<any>) => {
|
|
|
13
21
|
| null
|
|
14
22
|
| number
|
|
15
23
|
| React.Component<any, any>
|
|
16
|
-
| React.ComponentClass<any
|
|
24
|
+
| React.ComponentClass<any>
|
|
17
25
|
) => {
|
|
18
26
|
if (!component) {
|
|
19
27
|
return;
|
|
@@ -31,7 +39,7 @@ export const useFocus = (refComponent?: React.RefObject<any>) => {
|
|
|
31
39
|
} else if (__DEV__) {
|
|
32
40
|
console.warn(
|
|
33
41
|
// @ts-ignore
|
|
34
|
-
`${SHELL_COLORS.BG_RED}AMA.${SHELL_COLORS.RESET} ${SHELL_COLORS.BLUE}useFocus${SHELL_COLORS.RESET}: ${SHELL_COLORS.YELLOW}Ref element not found${SHELL_COLORS.RESET}
|
|
42
|
+
`${SHELL_COLORS.BG_RED}AMA.${SHELL_COLORS.RESET} ${SHELL_COLORS.BLUE}useFocus${SHELL_COLORS.RESET}: ${SHELL_COLORS.YELLOW}Ref element not found${SHELL_COLORS.RESET}`
|
|
35
43
|
);
|
|
36
44
|
}
|
|
37
45
|
} catch (error) {
|
|
@@ -39,13 +47,13 @@ export const useFocus = (refComponent?: React.RefObject<any>) => {
|
|
|
39
47
|
console.warn(
|
|
40
48
|
// @ts-ignore
|
|
41
49
|
`${SHELL_COLORS.BG_RED}AMA.${SHELL_COLORS.RESET} ${SHELL_COLORS.BLUE}useFocus${SHELL_COLORS.RESET}: ${SHELL_COLORS.YELLOW}Error finding node handle${SHELL_COLORS.RESET} \n`,
|
|
42
|
-
error
|
|
50
|
+
error
|
|
43
51
|
);
|
|
44
52
|
}
|
|
45
53
|
}
|
|
46
54
|
});
|
|
47
55
|
},
|
|
48
|
-
[]
|
|
56
|
+
[]
|
|
49
57
|
);
|
|
50
58
|
|
|
51
59
|
React.useEffect(() => {
|
package/src/index.ts
CHANGED
|
@@ -13,7 +13,9 @@ export {
|
|
|
13
13
|
export { HideChildrenFromAccessibilityTree } from './components/HideChildrenFromAccessibilityTree';
|
|
14
14
|
|
|
15
15
|
// Hooks
|
|
16
|
-
export { useButtonChecks } from './hooks/useButtonChecks';
|
|
17
|
-
export { useChecks } from './hooks/useChecks';
|
|
18
16
|
export { useFocus } from './hooks/useFocus';
|
|
19
17
|
export { useTimedAction } from './hooks/useTimedAction';
|
|
18
|
+
|
|
19
|
+
export { default } from './ReactNativeAmaModule';
|
|
20
|
+
export { default as ReactNativeAmaView } from './ReactNativeAmaView';
|
|
21
|
+
export * from './ReactNativeAma.types';
|