@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.
- package/dist/{core/src/components → components}/AMAProvider.js +1 -2
- package/dist/{core/src/index.js → index.js} +3 -3
- package/package.json +31 -6
- package/src/components/AMAProvider.d.ts +26 -0
- package/src/components/AMAProvider.integration.test.tsx +145 -0
- package/src/components/AMAProvider.js +147 -0
- package/src/components/AMAProvider.test.tsx +199 -0
- package/src/components/AMAProvider.tsx +229 -0
- package/src/components/AutofocusContainer.d.ts +9 -0
- package/src/components/AutofocusContainer.js +58 -0
- package/src/components/AutofocusContainer.test.tsx +69 -0
- package/src/components/AutofocusContainer.tsx +43 -0
- package/src/components/HideChildrenFromAccessibilityTree.d.ts +6 -0
- package/src/components/HideChildrenFromAccessibilityTree.js +40 -0
- package/src/components/HideChildrenFromAccessibilityTree.test.tsx +129 -0
- package/src/components/HideChildrenFromAccessibilityTree.tsx +55 -0
- package/src/hooks/useButtonChecks.d.ts +8 -0
- package/src/hooks/useButtonChecks.js +51 -0
- package/src/hooks/useButtonChecks.ts +73 -0
- package/src/hooks/useChecks.d.ts +15 -0
- package/src/hooks/useChecks.js +152 -0
- package/src/hooks/useChecks.test.ts +214 -0
- package/src/hooks/useChecks.ts +207 -0
- package/src/hooks/useFocus.d.ts +4 -0
- package/{dist/internal/src/utils/maybeGenerateStringFromElement.js → src/hooks/useFocus.js} +26 -22
- package/src/hooks/useFocus.test.ts +99 -0
- package/src/hooks/useFocus.ts +42 -0
- package/src/hooks/useTimedAction.android.test.ts +39 -0
- package/src/hooks/useTimedAction.d.ts +3 -0
- package/src/hooks/useTimedAction.ios.test.ts +46 -0
- package/src/hooks/useTimedAction.js +63 -0
- package/src/hooks/useTimedAction.ts +40 -0
- package/src/index.d.ts +7 -0
- package/src/index.js +20 -0
- package/src/index.ts +16 -0
- package/src/utils/numerify.test.ts +11 -0
- package/src/utils/numerify.ts +3 -0
- package/dist/internal/src/checks/checkAccessibilityRole.d.ts +0 -3
- package/dist/internal/src/checks/checkAccessibilityRole.js +0 -32
- package/dist/internal/src/checks/checkFocusTrap.d.ts +0 -8
- package/dist/internal/src/checks/checkFocusTrap.js +0 -34
- package/dist/internal/src/checks/checkForAccessibilityState.d.ts +0 -7
- package/dist/internal/src/checks/checkForAccessibilityState.js +0 -48
- package/dist/internal/src/checks/checkMinimumSize.d.ts +0 -3
- package/dist/internal/src/checks/checkMinimumSize.js +0 -29
- package/dist/internal/src/checks/contrastChecker.d.ts +0 -8
- package/dist/internal/src/checks/contrastChecker.js +0 -77
- package/dist/internal/src/checks/noUndefinedProperty.d.ts +0 -8
- package/dist/internal/src/checks/noUndefinedProperty.js +0 -14
- package/dist/internal/src/checks/uppercaseChecker.d.ts +0 -9
- package/dist/internal/src/checks/uppercaseChecker.js +0 -18
- package/dist/internal/src/checks/uppercaseStringChecker.d.ts +0 -8
- package/dist/internal/src/checks/uppercaseStringChecker.js +0 -29
- package/dist/internal/src/index.d.ts +0 -20
- package/dist/internal/src/index.js +0 -56
- package/dist/internal/src/types.d.ts +0 -17
- package/dist/internal/src/types.js +0 -2
- package/dist/internal/src/utils/applyStyle.d.ts +0 -10
- package/dist/internal/src/utils/applyStyle.js +0 -33
- package/dist/internal/src/utils/constants.d.ts +0 -3
- package/dist/internal/src/utils/constants.js +0 -32
- package/dist/internal/src/utils/error.style.d.ts +0 -6
- package/dist/internal/src/utils/error.style.js +0 -11
- package/dist/internal/src/utils/generateAccessibilityStateFromProp.d.ts +0 -9
- package/dist/internal/src/utils/generateAccessibilityStateFromProp.js +0 -35
- package/dist/internal/src/utils/getPropertyFromStyle.d.ts +0 -2
- package/dist/internal/src/utils/getPropertyFromStyle.js +0 -10
- package/dist/internal/src/utils/interpolateAnimationStates.d.ts +0 -2
- package/dist/internal/src/utils/interpolateAnimationStates.js +0 -43
- package/dist/internal/src/utils/isFocused.d.ts +0 -2
- package/dist/internal/src/utils/isFocused.js +0 -7
- package/dist/internal/src/utils/logger.d.ts +0 -16
- package/dist/internal/src/utils/logger.js +0 -53
- package/dist/internal/src/utils/logger.rules.d.ts +0 -15
- package/dist/internal/src/utils/logger.rules.js +0 -74
- package/dist/internal/src/utils/maybeGenerateStringFromElement.d.ts +0 -3
- package/dist/internal/src/utils/minimumTouchableSize.d.ts +0 -3
- package/dist/internal/src/utils/minimumTouchableSize.js +0 -9
- package/dist/{core/src/components → components}/AMAProvider.d.ts +0 -0
- package/dist/{core/src/components → components}/AutofocusContainer.d.ts +0 -0
- package/dist/{core/src/components → components}/AutofocusContainer.js +0 -0
- package/dist/{core/src/components → components}/HideChildrenFromAccessibilityTree.d.ts +0 -0
- package/dist/{core/src/components → components}/HideChildrenFromAccessibilityTree.js +0 -0
- package/dist/{core/src/hooks → hooks}/useButtonChecks.d.ts +0 -0
- package/dist/{core/src/hooks → hooks}/useButtonChecks.js +0 -0
- package/dist/{core/src/hooks → hooks}/useChecks.d.ts +0 -0
- package/dist/{core/src/hooks → hooks}/useChecks.js +0 -0
- package/dist/{core/src/hooks → hooks}/useFocus.d.ts +0 -0
- package/dist/{core/src/hooks → hooks}/useFocus.js +0 -0
- package/dist/{core/src/hooks → hooks}/useTimedAction.d.ts +0 -0
- package/dist/{core/src/hooks → hooks}/useTimedAction.js +0 -0
- package/dist/{core/src/index.d.ts → index.d.ts} +1 -1
- /package/dist/{core/src/utils → utils}/numerify.d.ts +0 -0
- /package/dist/{core/src/utils → utils}/numerify.js +0 -0
|
@@ -36,7 +36,6 @@ const eventsMapping = {
|
|
|
36
36
|
invertColorsChanged: 'isInvertColorsEnabled',
|
|
37
37
|
screenReaderChanged: 'isScreenReaderEnabled',
|
|
38
38
|
};
|
|
39
|
-
const isDev = __DEV__;
|
|
40
39
|
const isDevContextValue = (value) => value.trackError !== undefined;
|
|
41
40
|
exports.isDevContextValue = isDevContextValue;
|
|
42
41
|
const DEFAULT_VALUES = {
|
|
@@ -89,7 +88,7 @@ const AMAProvider = ({ children }) => {
|
|
|
89
88
|
};
|
|
90
89
|
}, []);
|
|
91
90
|
const [failedItems, setFailedItems] = React.useState([]);
|
|
92
|
-
if (
|
|
91
|
+
if (__DEV__) {
|
|
93
92
|
const trackError = (id) => {
|
|
94
93
|
if (failedItems.includes(id)) {
|
|
95
94
|
return;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.useTimedAction = exports.useFocus = exports.useChecks = exports.useButtonChecks = exports.
|
|
3
|
+
exports.useTimedAction = exports.useFocus = exports.useChecks = exports.useButtonChecks = exports.HideChildrenFromAccessibilityTree = exports.useAMAContext = exports.AMAProvider = exports.AutofocusContainer = void 0;
|
|
4
4
|
// Components
|
|
5
5
|
var AutofocusContainer_1 = require("./components/AutofocusContainer");
|
|
6
6
|
Object.defineProperty(exports, "AutofocusContainer", { enumerable: true, get: function () { return AutofocusContainer_1.AutofocusContainer; } });
|
|
7
|
-
var HideChildrenFromAccessibilityTree_1 = require("./components/HideChildrenFromAccessibilityTree");
|
|
8
|
-
Object.defineProperty(exports, "HideChildrenFromAccessibilityTree", { enumerable: true, get: function () { return HideChildrenFromAccessibilityTree_1.HideChildrenFromAccessibilityTree; } });
|
|
9
7
|
var AMAProvider_1 = require("./components/AMAProvider");
|
|
10
8
|
Object.defineProperty(exports, "AMAProvider", { enumerable: true, get: function () { return AMAProvider_1.AMAProvider; } });
|
|
11
9
|
Object.defineProperty(exports, "useAMAContext", { enumerable: true, get: function () { return AMAProvider_1.useAMAContext; } });
|
|
10
|
+
var HideChildrenFromAccessibilityTree_1 = require("./components/HideChildrenFromAccessibilityTree");
|
|
11
|
+
Object.defineProperty(exports, "HideChildrenFromAccessibilityTree", { enumerable: true, get: function () { return HideChildrenFromAccessibilityTree_1.HideChildrenFromAccessibilityTree; } });
|
|
12
12
|
// Hooks
|
|
13
13
|
var useButtonChecks_1 = require("./hooks/useButtonChecks");
|
|
14
14
|
Object.defineProperty(exports, "useButtonChecks", { enumerable: true, get: function () { return useButtonChecks_1.useButtonChecks; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-native-ama/core",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Accessible Mobile App Library for React Native",
|
|
5
5
|
"private": false,
|
|
6
6
|
"react-native": "src/index",
|
|
@@ -21,15 +21,16 @@
|
|
|
21
21
|
],
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build": "rm -rf dist && tsc -p ./tsconfig.build.json",
|
|
24
|
-
"typecheck": "tsc --noEmit"
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"test": "jest"
|
|
25
26
|
},
|
|
26
|
-
"
|
|
27
|
+
"dependencies": {
|
|
27
28
|
"@react-native-ama/internal": "*"
|
|
28
29
|
},
|
|
29
30
|
"peerDependencies": {
|
|
30
31
|
"react": "*",
|
|
31
32
|
"react-native": "*"
|
|
32
|
-
},
|
|
33
|
+
},
|
|
33
34
|
"keywords": [
|
|
34
35
|
"react-native",
|
|
35
36
|
"a11y",
|
|
@@ -48,6 +49,30 @@
|
|
|
48
49
|
"provenance": true
|
|
49
50
|
},
|
|
50
51
|
"author": "",
|
|
51
|
-
"license": "MIT"
|
|
52
|
-
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"babel-jest": "^29.7.0"
|
|
55
|
+
},
|
|
56
|
+
"jest": {
|
|
57
|
+
"preset": "react-native",
|
|
58
|
+
"modulePathIgnorePatterns": [
|
|
59
|
+
"<rootDir>/node_modules",
|
|
60
|
+
"<rootDir>/dist/"
|
|
61
|
+
],
|
|
62
|
+
"collectCoverageFrom": [
|
|
63
|
+
"src/**/*.{ts,tsx}"
|
|
64
|
+
],
|
|
65
|
+
"coverageThreshold": {
|
|
66
|
+
"global": {
|
|
67
|
+
"statements": 90,
|
|
68
|
+
"branches": 82,
|
|
69
|
+
"functions": 83,
|
|
70
|
+
"lines": 91
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"transformIgnorePatterns": [
|
|
74
|
+
"node_modules/(?!(@react-native|react-native|react-native-reanimated/))"
|
|
75
|
+
],
|
|
76
|
+
"verbose": true
|
|
77
|
+
}
|
|
53
78
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
type AMAProviderProps = {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
};
|
|
5
|
+
type SharedContextValue = {
|
|
6
|
+
isBoldTextEnabled: boolean;
|
|
7
|
+
isScreenReaderEnabled: boolean;
|
|
8
|
+
isGrayscaleEnabled: boolean;
|
|
9
|
+
isInvertColorsEnabled: boolean;
|
|
10
|
+
isReduceMotionEnabled: boolean;
|
|
11
|
+
isReduceTransparencyEnabled: boolean;
|
|
12
|
+
reactNavigationScreenOptions: {
|
|
13
|
+
animationEnabled: boolean;
|
|
14
|
+
animation: 'default' | 'fade';
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export type AMADevContextValue = SharedContextValue & {
|
|
18
|
+
trackError: (id: string) => void;
|
|
19
|
+
removeError: (id: string) => void;
|
|
20
|
+
};
|
|
21
|
+
export type AMAProdContextValue = SharedContextValue & {};
|
|
22
|
+
export type AMAContextValue = AMADevContextValue | AMAProdContextValue;
|
|
23
|
+
export declare const isDevContextValue: (value: AMAContextValue) => value is AMADevContextValue;
|
|
24
|
+
export declare const AMAProvider: React.FC<AMAProviderProps>;
|
|
25
|
+
export declare const useAMAContext: () => AMAContextValue;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { act, render } from '@testing-library/react-native';
|
|
2
|
+
import { flushMicroTasks } from '@testing-library/react-native/build/flush-micro-tasks';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
import { AMAContextValue, AMAProvider, useAMAContext } from './AMAProvider';
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
jest.clearAllMocks();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe('AMAProvider', () => {
|
|
12
|
+
describe('When __DEV__ is true', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
global.__DEV__ = true;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('trackError', () => {
|
|
19
|
+
it('exports trackError', async () => {
|
|
20
|
+
await renderAMAProvider();
|
|
21
|
+
|
|
22
|
+
expect(amaContextValuesRef.trackError).toBeDefined();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('shows the AMA error banner when called', async () => {
|
|
26
|
+
const renderAPI = await renderAMAProvider();
|
|
27
|
+
|
|
28
|
+
expect(renderAPI.queryByTestId('amaError')).toBeNull();
|
|
29
|
+
|
|
30
|
+
act(() => {
|
|
31
|
+
amaContextValuesRef.trackError('123');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
expect(renderAPI.getByTestId('amaError')).toBeDefined();
|
|
35
|
+
expect(
|
|
36
|
+
renderAPI.getByTestId('amaError').props.accessibilityLabel,
|
|
37
|
+
).toEqual("1 component(s) didn't pass the accessibility check(s)");
|
|
38
|
+
|
|
39
|
+
act(() => {
|
|
40
|
+
amaContextValuesRef.trackError('123');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
expect(
|
|
44
|
+
renderAPI.getByTestId('amaError').props.accessibilityLabel,
|
|
45
|
+
).toEqual("1 component(s) didn't pass the accessibility check(s)");
|
|
46
|
+
|
|
47
|
+
expect(renderAPI.getByTestId('amaError')).toBeDefined();
|
|
48
|
+
|
|
49
|
+
act(() => {
|
|
50
|
+
amaContextValuesRef.trackError('another');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(
|
|
54
|
+
renderAPI.getByTestId('amaError').props.accessibilityLabel,
|
|
55
|
+
).toEqual("2 component(s) didn't pass the accessibility check(s)");
|
|
56
|
+
|
|
57
|
+
expect(renderAPI.getByTestId('amaError')).toBeDefined();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('removeError', () => {
|
|
62
|
+
it('exports removeError', async () => {
|
|
63
|
+
await renderAMAProvider();
|
|
64
|
+
|
|
65
|
+
expect(amaContextValuesRef.removeError).toBeDefined();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('shows the AMA error banner when called', async () => {
|
|
69
|
+
const renderAPI = await renderAMAProvider();
|
|
70
|
+
|
|
71
|
+
expect(renderAPI.queryByTestId('amaError')).toBeNull();
|
|
72
|
+
|
|
73
|
+
act(() => {
|
|
74
|
+
amaContextValuesRef.trackError('123');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(
|
|
78
|
+
renderAPI.getByTestId('amaError').props.accessibilityLabel,
|
|
79
|
+
).toEqual("1 component(s) didn't pass the accessibility check(s)");
|
|
80
|
+
expect(renderAPI.getByTestId('amaError')).toBeDefined();
|
|
81
|
+
|
|
82
|
+
act(() => {
|
|
83
|
+
amaContextValuesRef.removeError('invalidID');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(
|
|
87
|
+
renderAPI.getByTestId('amaError').props.accessibilityLabel,
|
|
88
|
+
).toEqual("1 component(s) didn't pass the accessibility check(s)");
|
|
89
|
+
|
|
90
|
+
expect(renderAPI.getByTestId('amaError')).toBeDefined();
|
|
91
|
+
|
|
92
|
+
act(() => {
|
|
93
|
+
amaContextValuesRef.removeError('123');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(renderAPI.queryByTestId('amaError')).toBeNull();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe('When __DEV__ is false', () => {
|
|
102
|
+
beforeEach(() => {
|
|
103
|
+
// @ts-ignore
|
|
104
|
+
global.__DEV__ = false;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('does not export trackError', async () => {
|
|
108
|
+
await renderAMAProvider();
|
|
109
|
+
|
|
110
|
+
expect(amaContextValuesRef.trackError).toBeUndefined();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('does not export removeError', async () => {
|
|
114
|
+
await renderAMAProvider();
|
|
115
|
+
|
|
116
|
+
expect(amaContextValuesRef.removeError).toBeUndefined();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const renderAMAProvider = async () => {
|
|
122
|
+
const result = render(
|
|
123
|
+
<AMAProvider>
|
|
124
|
+
<DummyComponent />
|
|
125
|
+
</AMAProvider>,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
await act(async () => {
|
|
129
|
+
await flushMicroTasks();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return result;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
let amaContextValuesRef: Extract<
|
|
136
|
+
AMAContextValue,
|
|
137
|
+
{ trackError: Function; removeError: Function }
|
|
138
|
+
>;
|
|
139
|
+
|
|
140
|
+
const DummyComponent = () => {
|
|
141
|
+
// @ts-ignore
|
|
142
|
+
amaContextValuesRef = useAMAContext();
|
|
143
|
+
|
|
144
|
+
return null;
|
|
145
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable react-native/no-inline-styles */
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
20
|
+
if (mod && mod.__esModule) return mod;
|
|
21
|
+
var result = {};
|
|
22
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
23
|
+
__setModuleDefault(result, mod);
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.useAMAContext = exports.AMAProvider = exports.isDevContextValue = void 0;
|
|
28
|
+
const internal_1 = require("@react-native-ama/internal");
|
|
29
|
+
const React = __importStar(require("react"));
|
|
30
|
+
const react_native_1 = require("react-native");
|
|
31
|
+
const eventsMapping = {
|
|
32
|
+
reduceTransparencyChanged: 'isReduceTransparencyEnabled',
|
|
33
|
+
reduceMotionChanged: 'isReduceMotionEnabled',
|
|
34
|
+
grayscaleChanged: 'isGrayscaleEnabled',
|
|
35
|
+
boldTextChanged: 'isBoldTextEnabled',
|
|
36
|
+
invertColorsChanged: 'isInvertColorsEnabled',
|
|
37
|
+
screenReaderChanged: 'isScreenReaderEnabled',
|
|
38
|
+
};
|
|
39
|
+
const isDevContextValue = (value) => value.trackError !== undefined;
|
|
40
|
+
exports.isDevContextValue = isDevContextValue;
|
|
41
|
+
const DEFAULT_VALUES = {
|
|
42
|
+
isReduceTransparencyEnabled: false,
|
|
43
|
+
isBoldTextEnabled: false,
|
|
44
|
+
isGrayscaleEnabled: false,
|
|
45
|
+
isInvertColorsEnabled: false,
|
|
46
|
+
isReduceMotionEnabled: false,
|
|
47
|
+
isScreenReaderEnabled: false,
|
|
48
|
+
reactNavigationScreenOptions: {
|
|
49
|
+
animationEnabled: true,
|
|
50
|
+
animation: 'default',
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
const AMAContext = React.createContext(null);
|
|
54
|
+
const AMAProvider = ({ children }) => {
|
|
55
|
+
const [values, setValues] = React.useState(DEFAULT_VALUES);
|
|
56
|
+
const handleAccessibilityInfoChanged = (key) => {
|
|
57
|
+
return (newValue) => {
|
|
58
|
+
setValues(oldValues => {
|
|
59
|
+
const newValues = Object.assign({}, oldValues);
|
|
60
|
+
newValues[key] = newValue;
|
|
61
|
+
if (key === 'isReduceMotionEnabled') {
|
|
62
|
+
newValues.reactNavigationScreenOptions.animationEnabled = !newValue;
|
|
63
|
+
newValues.reactNavigationScreenOptions.animation = newValue
|
|
64
|
+
? 'fade'
|
|
65
|
+
: 'default';
|
|
66
|
+
}
|
|
67
|
+
return Object.assign(Object.assign({}, oldValues), newValues);
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
const allInitPromises = [];
|
|
73
|
+
const subscriptions = Object.entries(eventsMapping).map(([eventName, contextKey]) => {
|
|
74
|
+
allInitPromises.push(react_native_1.AccessibilityInfo[contextKey]());
|
|
75
|
+
return react_native_1.AccessibilityInfo.addEventListener(eventName, handleAccessibilityInfoChanged(contextKey));
|
|
76
|
+
});
|
|
77
|
+
Promise.all(allInitPromises).then(promisesValues => {
|
|
78
|
+
const newValues = Object.values(eventsMapping).reduce((list, key, index) => {
|
|
79
|
+
list[key] = promisesValues[index];
|
|
80
|
+
return list;
|
|
81
|
+
}, {});
|
|
82
|
+
setValues(oldValues => {
|
|
83
|
+
return Object.assign(Object.assign({}, oldValues), newValues);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
return () => {
|
|
87
|
+
subscriptions.forEach(subscription => subscription === null || subscription === void 0 ? void 0 : subscription.remove());
|
|
88
|
+
};
|
|
89
|
+
}, []);
|
|
90
|
+
const [failedItems, setFailedItems] = React.useState([]);
|
|
91
|
+
if (__DEV__) {
|
|
92
|
+
const trackError = (id) => {
|
|
93
|
+
if (failedItems.includes(id)) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
setFailedItems(items => [...items, id]);
|
|
97
|
+
react_native_1.AccessibilityInfo.announceForAccessibility("One or more component didn't pass the accessibility check, please check the console for more info");
|
|
98
|
+
};
|
|
99
|
+
const removeError = (id) => {
|
|
100
|
+
setFailedItems(items => {
|
|
101
|
+
const index = items.indexOf(id);
|
|
102
|
+
if (index >= 0) {
|
|
103
|
+
items.splice(index);
|
|
104
|
+
return [...items];
|
|
105
|
+
}
|
|
106
|
+
return items;
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
return (<AMAContext.Provider value={Object.assign(Object.assign({}, values), { trackError,
|
|
110
|
+
removeError })}>
|
|
111
|
+
<react_native_1.View style={{ flex: 1 }}>
|
|
112
|
+
<>
|
|
113
|
+
{children}
|
|
114
|
+
{failedItems.length > 0 ? (<AMAError count={failedItems.length}/>) : null}
|
|
115
|
+
</>
|
|
116
|
+
</react_native_1.View>
|
|
117
|
+
</AMAContext.Provider>);
|
|
118
|
+
}
|
|
119
|
+
return <AMAContext.Provider value={values}>{children}</AMAContext.Provider>;
|
|
120
|
+
};
|
|
121
|
+
exports.AMAProvider = AMAProvider;
|
|
122
|
+
const AMAError = ({ count }) => {
|
|
123
|
+
const error = `${count} component(s) didn't pass the accessibility check(s)`;
|
|
124
|
+
return (<react_native_1.View accessibilityLabel={error} accessibilityHint="Please check the console for more info..." style={{
|
|
125
|
+
paddingHorizontal: 24,
|
|
126
|
+
paddingTop: 24,
|
|
127
|
+
paddingBottom: 48,
|
|
128
|
+
backgroundColor: internal_1.RED,
|
|
129
|
+
}} testID="amaError">
|
|
130
|
+
<react_native_1.View accessible={true}>
|
|
131
|
+
<react_native_1.Text style={{ color: 'white', fontSize: 16, lineHeight: 26 }} testID="amaError.message">
|
|
132
|
+
{error}
|
|
133
|
+
</react_native_1.Text>
|
|
134
|
+
<react_native_1.Text style={{ color: 'white', fontSize: 16, lineHeight: 24 }}>
|
|
135
|
+
Please check the console for more info...
|
|
136
|
+
</react_native_1.Text>
|
|
137
|
+
</react_native_1.View>
|
|
138
|
+
</react_native_1.View>);
|
|
139
|
+
};
|
|
140
|
+
const useAMAContext = () => {
|
|
141
|
+
const context = React.useContext(AMAContext);
|
|
142
|
+
if (!context) {
|
|
143
|
+
throw new Error('Please wrap your app with <AMAProvider />');
|
|
144
|
+
}
|
|
145
|
+
return context;
|
|
146
|
+
};
|
|
147
|
+
exports.useAMAContext = useAMAContext;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { act, render } from '@testing-library/react-native';
|
|
2
|
+
import { flushMicroTasks } from '@testing-library/react-native/build/flush-micro-tasks';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { AccessibilityInfo } from 'react-native';
|
|
5
|
+
|
|
6
|
+
import { AMAContextValue, AMAProvider } from './AMAProvider';
|
|
7
|
+
|
|
8
|
+
var mockProvider: jest.Mock;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
jest.clearAllMocks();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe('AMAProvider', () => {
|
|
15
|
+
it('adds event listener for "reduceMotionChanged"', async () => {
|
|
16
|
+
const spy = jest
|
|
17
|
+
.spyOn(AccessibilityInfo, 'addEventListener')
|
|
18
|
+
.mockReturnValue({ remove: jest.fn() } as any);
|
|
19
|
+
|
|
20
|
+
await renderAMAProvider();
|
|
21
|
+
|
|
22
|
+
expect(spy).toHaveBeenCalledWith(
|
|
23
|
+
'reduceMotionChanged',
|
|
24
|
+
expect.any(Function),
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('adds event listener for "screenReaderChanged"', async () => {
|
|
29
|
+
const spy = jest
|
|
30
|
+
.spyOn(AccessibilityInfo, 'addEventListener')
|
|
31
|
+
.mockReturnValue({ remove: jest.fn() } as any);
|
|
32
|
+
|
|
33
|
+
await renderAMAProvider();
|
|
34
|
+
|
|
35
|
+
expect(spy).toHaveBeenCalledWith(
|
|
36
|
+
'screenReaderChanged',
|
|
37
|
+
expect.any(Function),
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('setups the initial value for all the states', async () => {
|
|
42
|
+
const events = [
|
|
43
|
+
'isReduceMotionEnabled',
|
|
44
|
+
'isScreenReaderEnabled',
|
|
45
|
+
'isReduceTransparencyEnabled',
|
|
46
|
+
'isGrayscaleEnabled',
|
|
47
|
+
'isBoldTextEnabled',
|
|
48
|
+
'isInvertColorsEnabled',
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const states: any = {};
|
|
52
|
+
|
|
53
|
+
events.forEach(event => {
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
jest.spyOn(AccessibilityInfo, event).mockResolvedValue('yay');
|
|
56
|
+
states[event] = 'yay';
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
await renderAMAProvider();
|
|
60
|
+
|
|
61
|
+
expect(mockProvider.mock.calls[1][0]).toMatchObject({
|
|
62
|
+
value: states,
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it.each`
|
|
67
|
+
eventName | valueName
|
|
68
|
+
${'reduceMotionChanged'} | ${'isReduceMotionEnabled'}
|
|
69
|
+
${'screenReaderChanged'} | ${'isScreenReaderEnabled'}
|
|
70
|
+
${'reduceTransparencyChanged'} | ${'isReduceTransparencyEnabled'}
|
|
71
|
+
${'grayscaleChanged'} | ${'isGrayscaleEnabled'}
|
|
72
|
+
${'boldTextChanged'} | ${'isBoldTextEnabled'}
|
|
73
|
+
${'invertColorsChanged'} | ${'isInvertColorsEnabled'}
|
|
74
|
+
`(
|
|
75
|
+
'reacts to the events change $eventName',
|
|
76
|
+
async ({
|
|
77
|
+
eventName,
|
|
78
|
+
valueName,
|
|
79
|
+
}: {
|
|
80
|
+
eventName: string;
|
|
81
|
+
valueName: keyof AMAContextValue;
|
|
82
|
+
}) => {
|
|
83
|
+
let handler: Function;
|
|
84
|
+
jest
|
|
85
|
+
.spyOn(AccessibilityInfo, 'addEventListener')
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
.mockImplementation((name, callback) => {
|
|
88
|
+
if (name === eventName) {
|
|
89
|
+
handler = callback;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
remove: jest.fn(),
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
await renderAMAProvider();
|
|
98
|
+
|
|
99
|
+
const state: any = {};
|
|
100
|
+
state[valueName] = false;
|
|
101
|
+
|
|
102
|
+
expect(mockProvider).toHaveBeenCalledWith(
|
|
103
|
+
expect.objectContaining({
|
|
104
|
+
value: expect.objectContaining(state),
|
|
105
|
+
}),
|
|
106
|
+
{},
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
jest.clearAllMocks();
|
|
110
|
+
|
|
111
|
+
act(() => {
|
|
112
|
+
handler!(true);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
state[valueName] = true;
|
|
116
|
+
expect(mockProvider).toHaveBeenCalledWith(
|
|
117
|
+
expect.objectContaining({
|
|
118
|
+
value: expect.objectContaining(state),
|
|
119
|
+
}),
|
|
120
|
+
{},
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
jest.clearAllMocks();
|
|
124
|
+
|
|
125
|
+
act(() => {
|
|
126
|
+
handler!(false);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
state[valueName] = false;
|
|
130
|
+
expect(mockProvider).toHaveBeenCalledWith(
|
|
131
|
+
expect.objectContaining({
|
|
132
|
+
value: expect.objectContaining(state),
|
|
133
|
+
}),
|
|
134
|
+
{},
|
|
135
|
+
);
|
|
136
|
+
},
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
it.each`
|
|
140
|
+
eventName
|
|
141
|
+
${'reduceMotionChanged'}
|
|
142
|
+
${'screenReaderChanged'}
|
|
143
|
+
${'reduceTransparencyChanged'}
|
|
144
|
+
${'grayscaleChanged'}
|
|
145
|
+
${'boldTextChanged'}
|
|
146
|
+
${'invertColorsChanged'}
|
|
147
|
+
`(
|
|
148
|
+
'remove the "$eventName" subscription when the provider is unmounted',
|
|
149
|
+
async ({ eventName }) => {
|
|
150
|
+
const removeMock = jest.fn();
|
|
151
|
+
jest
|
|
152
|
+
.spyOn(AccessibilityInfo, 'addEventListener')
|
|
153
|
+
// @ts-ignore
|
|
154
|
+
.mockImplementation((name, _callback) => {
|
|
155
|
+
return { remove: name === eventName ? removeMock : jest.fn() };
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const renderAPI = await renderAMAProvider();
|
|
159
|
+
renderAPI.unmount();
|
|
160
|
+
|
|
161
|
+
expect(removeMock).toHaveBeenCalledWith();
|
|
162
|
+
},
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const renderAMAProvider = async () => {
|
|
167
|
+
const result = render(
|
|
168
|
+
<AMAProvider>
|
|
169
|
+
<></>
|
|
170
|
+
</AMAProvider>,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
await act(async () => {
|
|
174
|
+
await flushMicroTasks();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return result;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
function mockCreateContext() {
|
|
181
|
+
const original = jest.requireActual('react');
|
|
182
|
+
|
|
183
|
+
mockProvider = jest.fn().mockReturnValue(null);
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
...original,
|
|
187
|
+
createContext: () => {
|
|
188
|
+
return {
|
|
189
|
+
Provider: mockProvider,
|
|
190
|
+
Consumer: () => {},
|
|
191
|
+
displayName: '',
|
|
192
|
+
};
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
jest.mock('react', () => {
|
|
198
|
+
return mockCreateContext();
|
|
199
|
+
});
|