@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,207 @@
1
+ import {
2
+ ERROR_STYLE,
3
+ LogParams,
4
+ checkAccessibilityRole as checkAccessibilityRoleImplementation,
5
+ checkFocusTrap as checkFocusTrapImplementation,
6
+ checkForAccessibilityState,
7
+ checkMinimumSize as checkMinimumSizeImplementation,
8
+ contrastChecker as contrastCheckerImplementation,
9
+ getRuleAction,
10
+ logFailure,
11
+ noUndefinedProperty as noUndefinedPropertyImplementation,
12
+ uppercaseStringChecker as noUppercaseStringCheckerImplementation,
13
+ uppercaseChecker as uppercaseCheckerImplementation,
14
+ type CheckFocusTrap,
15
+ type CheckForAccessibilityState,
16
+ type ContrastChecker,
17
+ type NoUndefinedProperty,
18
+ type UppercaseChecker,
19
+ type UppercaseStringChecker,
20
+ } from '@react-native-ama/internal';
21
+ import * as React from 'react';
22
+ import {
23
+ AccessibilityRole,
24
+ InteractionManager,
25
+ LayoutChangeEvent,
26
+ } from 'react-native';
27
+
28
+ import { isDevContextValue, useAMAContext } from '../components/AMAProvider';
29
+
30
+ export const useChecks = () => {
31
+ const context = useAMAContext();
32
+
33
+ if (!isDevContextValue(context)) {
34
+ return null;
35
+ }
36
+
37
+ /**
38
+ * Because the AMA Error view cannot be removed by default, this hacky solution
39
+ * allows the dev to hide it if all the issues have been solved.
40
+ */
41
+ const fakeRandom = React.useRef('' + Date.now() + Math.random() * 42);
42
+ const hasErrors = React.useRef(false);
43
+ const failedTests = React.useRef<string[]>([]);
44
+ const shouldCheckLayout = React.useRef(true);
45
+ const layoutCheckTimeout = React.useRef<NodeJS.Timeout>();
46
+ const [minimumSizeFailed, setMinimumSizeFailed] = React.useState(false);
47
+ const [debugStyle, setDebugStyle] = React.useState<any>({});
48
+
49
+ const { trackError, removeError } = context;
50
+
51
+ const logResult = (
52
+ name: string,
53
+ result: LogParams | LogParams[] | null,
54
+ ): Record<string, any> => {
55
+ const index = failedTests.current.indexOf(name);
56
+
57
+ if (result === null) {
58
+ if (index >= 0 && hasErrors.current) {
59
+ failedTests.current.splice(index);
60
+
61
+ hasErrors.current = false;
62
+
63
+ InteractionManager.runAfterInteractions(() => {
64
+ removeError(fakeRandom.current);
65
+
66
+ setTimeout(() => {
67
+ setDebugStyle({});
68
+ }, 100);
69
+ });
70
+ }
71
+ }
72
+
73
+ const results = Array.isArray(result) ? result : [result];
74
+
75
+ results.forEach(logParam => {
76
+ if (logParam === null) {
77
+ return;
78
+ }
79
+
80
+ const action = getRuleAction?.(logParam.rule);
81
+ const hasFailed = action === 'MUST_NOT' || action === 'MUST';
82
+
83
+ if (index < 0) {
84
+ // @ts-ignore
85
+ logFailure?.({ action, ...logParam });
86
+ }
87
+
88
+ if (!hasFailed) {
89
+ return;
90
+ } else if (index >= 0) {
91
+ return;
92
+ }
93
+
94
+ failedTests.current.push(name);
95
+
96
+ InteractionManager.runAfterInteractions(() => {
97
+ trackError(fakeRandom.current);
98
+
99
+ setDebugStyle(ERROR_STYLE);
100
+ });
101
+
102
+ hasErrors.current = true;
103
+ });
104
+
105
+ // @ts-ignore
106
+ return hasErrors.current ? ERROR_STYLE : {};
107
+ };
108
+
109
+ const noUndefinedProperty = <T>(params: NoUndefinedProperty<T>) =>
110
+ logResult(
111
+ `noUndefinedProperty ${params.property as string}`,
112
+ noUndefinedPropertyImplementation(params),
113
+ );
114
+
115
+ const contrastChecker = (params: ContrastChecker) =>
116
+ logResult('contrastChecker', contrastCheckerImplementation(params));
117
+
118
+ const checkMinimumSize = (params: LayoutChangeEvent) => {
119
+ return logResult(
120
+ 'checkMinimumSize',
121
+ checkMinimumSizeImplementation(params),
122
+ );
123
+ };
124
+
125
+ const noUppercaseStringChecker = (params: UppercaseStringChecker) =>
126
+ logResult(
127
+ 'accessibilityLabelChecker',
128
+ noUppercaseStringCheckerImplementation(params),
129
+ );
130
+
131
+ const uppercaseChecker = (params: UppercaseChecker) =>
132
+ logResult('uppercaseChecker', uppercaseCheckerImplementation(params));
133
+
134
+ const checkFocusTrap = (params: CheckFocusTrap) => {
135
+ checkFocusTrapImplementation(params).then(result => {
136
+ logResult('checkFocusTrap', result);
137
+ });
138
+ };
139
+
140
+ const checkAccessibilityRole = (param: AccessibilityRole) => {
141
+ logResult(
142
+ 'checkAccessibilityRole',
143
+ checkAccessibilityRoleImplementation(param),
144
+ );
145
+ };
146
+
147
+ const checkCompatibleAccessibilityState = (props: Record<string, any>) => {
148
+ const amaStates = ['checked', 'selected'];
149
+
150
+ const params: CheckForAccessibilityState = {
151
+ accessibilityRole: props.accessibilityRole,
152
+ accessibilityState: props?.accessibilityState,
153
+ };
154
+
155
+ amaStates.forEach(amaState => {
156
+ if (amaState in props) {
157
+ // @ts-ignore
158
+ params[amaState] = props[amaState];
159
+ }
160
+ });
161
+
162
+ logResult(
163
+ 'checkCompatibleAccessibilityState',
164
+ checkForAccessibilityState(params),
165
+ );
166
+ };
167
+
168
+ const onLayout = (event: LayoutChangeEvent) => {
169
+ /**
170
+ * When the check fails there are situation when adding a border makes the
171
+ * component meet the minimum size requirement, and this causes a loop as the
172
+ * state is update continuously between true and false.
173
+ * To "avoid" that we wait at least 100ms before checking the size again, as
174
+ * this gives the dev time to hot-reload the changes.
175
+ */
176
+ if (!shouldCheckLayout.current) {
177
+ return;
178
+ }
179
+
180
+ shouldCheckLayout.current = false;
181
+
182
+ // @ts-ignore
183
+ clearTimeout(layoutCheckTimeout.current);
184
+
185
+ layoutCheckTimeout.current = setTimeout(() => {
186
+ shouldCheckLayout.current = true;
187
+ }, 1000);
188
+
189
+ const result = checkMinimumSize(event);
190
+
191
+ setMinimumSizeFailed(Object.keys(result).length > 0);
192
+ };
193
+
194
+ return {
195
+ logResult,
196
+ noUndefinedProperty,
197
+ contrastChecker,
198
+ onLayout,
199
+ noUppercaseStringChecker,
200
+ uppercaseChecker,
201
+ checkFocusTrap,
202
+ minimumSizeFailed,
203
+ checkCompatibleAccessibilityState,
204
+ checkAccessibilityRole,
205
+ debugStyle,
206
+ };
207
+ };
@@ -0,0 +1,4 @@
1
+ import * as React from 'react';
2
+ export declare const useFocus: (refComponent?: React.RefObject<any>) => {
3
+ setFocus: (component: null | number | React.Component<any, any> | React.ComponentClass<any>) => void;
4
+ };
@@ -23,29 +23,33 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.maybeGenerateStringFromElement = void 0;
26
+ exports.useFocus = void 0;
27
+ const internal_1 = require("@react-native-ama/internal");
27
28
  const React = __importStar(require("react"));
28
- const maybeGenerateStringFromElement = (element, alternativeString) => {
29
- var _a;
30
- if (alternativeString || element == null) {
31
- return alternativeString || '';
32
- }
33
- const pieces = extractPieces(element);
34
- if (pieces == null || (pieces === null || pieces === void 0 ? void 0 : pieces.length) === 0) {
35
- console.error(`Cannot generate the string from ${element} prop. Please provide one!`);
36
- }
37
- return (_a = pieces === null || pieces === void 0 ? void 0 : pieces.join(' ')) === null || _a === void 0 ? void 0 : _a.replace(/\*$/, '');
38
- };
39
- exports.maybeGenerateStringFromElement = maybeGenerateStringFromElement;
40
- const extractPieces = (component) => {
41
- return React.Children.map(component, child => {
42
- if (!React.isValidElement(child)) {
43
- return '';
29
+ const react_native_1 = require("react-native");
30
+ const useFocus = (refComponent) => {
31
+ const setFocus = React.useCallback((component) => {
32
+ // @ts-ignore
33
+ const elementId = (0, react_native_1.findNodeHandle)(component);
34
+ if (elementId) {
35
+ react_native_1.AccessibilityInfo.setAccessibilityFocus(elementId);
36
+ react_native_1.AccessibilityInfo.setAccessibilityFocus(elementId);
37
+ }
38
+ else if (__DEV__) {
39
+ console.warn(
40
+ // @ts-ignore
41
+ `${internal_1.SHELL_COLORS.BG_RED}AMA.${internal_1.SHELL_COLORS.RESET} ${internal_1.SHELL_COLORS.BLUE}useFocus${internal_1.SHELL_COLORS.RESET}: ${internal_1.SHELL_COLORS.YELLOW}Ref element not found${internal_1.SHELL_COLORS.RESET}`);
44
42
  }
45
- const children = child.props.children;
46
- if (React.isValidElement(children) || Array.isArray(children)) {
47
- return extractPieces(children);
43
+ }, []);
44
+ React.useEffect(() => {
45
+ if (!refComponent) {
46
+ return;
48
47
  }
49
- return children;
50
- });
48
+ setFocus(refComponent.current);
49
+ // eslint-disable-next-line react-hooks/exhaustive-deps
50
+ }, [refComponent]);
51
+ return {
52
+ setFocus,
53
+ };
51
54
  };
55
+ exports.useFocus = useFocus;
@@ -0,0 +1,99 @@
1
+ import { SHELL_COLORS } from '@react-native-ama/internal';
2
+ import { renderHook } from '@testing-library/react-native';
3
+
4
+ import { useFocus } from './useFocus';
5
+
6
+ beforeEach(() => {
7
+ jest.clearAllMocks();
8
+
9
+ jest.spyOn(console, 'warn').mockImplementation();
10
+ });
11
+
12
+ let findNodeHandleMock: jest.Mock;
13
+ let setAccessibilityFocusMock: jest.Mock;
14
+
15
+ describe('useFocus', () => {
16
+ it.each([null, undefined])('does nothing if the Ref is %s', refElement => {
17
+ // @ts-ignore
18
+ renderHook(() => useFocus(refElement));
19
+
20
+ expect(findNodeHandleMock).not.toHaveBeenCalled();
21
+ });
22
+
23
+ it('call setAccessibilityFocus on the given Ref Component', () => {
24
+ findNodeHandleMock.mockReturnValue('element-id');
25
+
26
+ renderHook(() => useFocus({ current: 'test-element' }));
27
+
28
+ expect(findNodeHandleMock).toHaveBeenCalledWith('test-element');
29
+ expect(setAccessibilityFocusMock).toHaveBeenNthCalledWith(1, 'element-id');
30
+ expect(setAccessibilityFocusMock).toHaveBeenNthCalledWith(2, 'element-id');
31
+ });
32
+
33
+ describe('Given the element has not been found', () => {
34
+ beforeEach(function () {
35
+ findNodeHandleMock.mockReturnValue(null);
36
+ });
37
+
38
+ it('does not call setAccessibilityFocus', () => {
39
+ renderHook(() => useFocus({ current: 'test-element' }));
40
+
41
+ expect(setAccessibilityFocusMock).not.toHaveBeenCalled();
42
+ });
43
+
44
+ it('console.warn if __DEV__ is true', () => {
45
+ const spy = jest.spyOn(console, 'warn').mockImplementation();
46
+
47
+ renderHook(() => useFocus({ current: 'test-element' }));
48
+
49
+ expect(spy).toHaveBeenCalledWith(
50
+ // @ts-ignore
51
+ `${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}`,
52
+ );
53
+ });
54
+
55
+ it('does not log anything if __DEV__ is false', () => {
56
+ // @ts-ignore
57
+ global.__DEV__ = false;
58
+
59
+ const spy = jest.spyOn(console, 'warn');
60
+
61
+ renderHook(() => useFocus({ current: 'test-element' }));
62
+
63
+ expect(spy).not.toHaveBeenCalled();
64
+ });
65
+ });
66
+
67
+ it('setFocus calls setAccessibilityFocus on the given component', () => {
68
+ findNodeHandleMock.mockReturnValue('test-focus-id');
69
+
70
+ const { result } = renderHook(() => useFocus());
71
+
72
+ result.current.setFocus('component' as any);
73
+
74
+ expect(findNodeHandleMock).toHaveBeenCalledWith('component');
75
+ expect(setAccessibilityFocusMock).toHaveBeenNthCalledWith(
76
+ 1,
77
+ 'test-focus-id',
78
+ );
79
+ expect(setAccessibilityFocusMock).toHaveBeenNthCalledWith(
80
+ 2,
81
+ 'test-focus-id',
82
+ );
83
+ });
84
+ });
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());
@@ -0,0 +1,42 @@
1
+ import { SHELL_COLORS } from '@react-native-ama/internal';
2
+ import * as React from 'react';
3
+ import { AccessibilityInfo, findNodeHandle } from 'react-native';
4
+
5
+ export const useFocus = (refComponent?: React.RefObject<any>) => {
6
+ const setFocus = React.useCallback(
7
+ (
8
+ component:
9
+ | null
10
+ | number
11
+ | React.Component<any, any>
12
+ | React.ComponentClass<any>,
13
+ ) => {
14
+ // @ts-ignore
15
+ const elementId = findNodeHandle(component);
16
+
17
+ if (elementId) {
18
+ AccessibilityInfo.setAccessibilityFocus(elementId);
19
+ AccessibilityInfo.setAccessibilityFocus(elementId);
20
+ } else if (__DEV__) {
21
+ console.warn(
22
+ // @ts-ignore
23
+ `${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}`,
24
+ );
25
+ }
26
+ },
27
+ [],
28
+ );
29
+
30
+ React.useEffect(() => {
31
+ if (!refComponent) {
32
+ return;
33
+ }
34
+
35
+ setFocus(refComponent.current);
36
+ // eslint-disable-next-line react-hooks/exhaustive-deps
37
+ }, [refComponent]);
38
+
39
+ return {
40
+ setFocus,
41
+ };
42
+ };
@@ -0,0 +1,39 @@
1
+ import { renderHook } from '@testing-library/react-native';
2
+ import { AccessibilityInfo, Platform } from 'react-native';
3
+
4
+ import * as AMAProvider from '../components/AMAProvider';
5
+ import { useTimedAction } from './useTimedAction';
6
+
7
+ beforeEach(() => {
8
+ jest.clearAllMocks();
9
+
10
+ Platform.OS = 'android';
11
+ });
12
+
13
+ describe('useTimedAction', () => {
14
+ it.each([true, false])(
15
+ 'onTimeout uses the value provided by getRecommendedTimeoutMillis for the timeout',
16
+ async isScreenReaderEnabled => {
17
+ const callback = jest.fn();
18
+
19
+ jest.spyOn(AMAProvider, 'useAMAContext').mockReturnValue({
20
+ isScreenReaderEnabled,
21
+ } as any);
22
+
23
+ const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
24
+
25
+ const getRecommendedTimeoutMillis = jest
26
+ .spyOn(AccessibilityInfo, 'getRecommendedTimeoutMillis')
27
+ .mockResolvedValue(142);
28
+
29
+ const { result } = renderHook(() => useTimedAction());
30
+
31
+ await result.current.onTimeout(callback, 42);
32
+
33
+ expect(getRecommendedTimeoutMillis).toHaveBeenCalledWith(42);
34
+ expect(setTimeoutSpy).toHaveBeenCalledWith(callback, 142);
35
+ },
36
+ );
37
+ });
38
+
39
+ jest.mock('../components/AMAProvider');
@@ -0,0 +1,3 @@
1
+ export declare const useTimedAction: () => {
2
+ onTimeout: (callback: () => void, milliseconds: number) => Promise<null | ReturnType<typeof setTimeout>>;
3
+ };
@@ -0,0 +1,46 @@
1
+ import { renderHook } from '@testing-library/react-native';
2
+ import { Platform } from 'react-native';
3
+
4
+ import * as AMAProvider from '../components/AMAProvider';
5
+ import { useTimedAction } from './useTimedAction';
6
+
7
+ beforeEach(() => {
8
+ jest.clearAllMocks();
9
+
10
+ Platform.OS = 'ios';
11
+ });
12
+
13
+ describe('useTimedAction', () => {
14
+ it('onTimeout executes the callback with the given timeout if the screen reader is off', async () => {
15
+ const callback = jest.fn();
16
+ //TODO: AMAProvider does not export useAMAContext
17
+ const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
18
+ jest.spyOn(AMAProvider, 'useAMAContext').mockReturnValue({
19
+ isScreenReaderEnabled: false,
20
+ } as any);
21
+
22
+ const { result } = renderHook(() => useTimedAction());
23
+
24
+ setTimeoutSpy.mockClear();
25
+ await result.current.onTimeout(callback, 42);
26
+
27
+ expect(setTimeoutSpy).toHaveBeenCalledWith(callback, 42);
28
+ });
29
+
30
+ it('onTimeout does not executes the callback when the screen reader is off', async () => {
31
+ const callback = jest.fn();
32
+
33
+ const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
34
+ jest.spyOn(AMAProvider, 'useAMAContext').mockReturnValue({
35
+ isScreenReaderEnabled: true,
36
+ } as any);
37
+
38
+ const { result } = renderHook(() => useTimedAction());
39
+
40
+ setTimeoutSpy.mockClear();
41
+
42
+ await result.current.onTimeout(callback, 42);
43
+
44
+ expect(setTimeoutSpy).not.toHaveBeenCalled();
45
+ });
46
+ });
@@ -0,0 +1,63 @@
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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ exports.useTimedAction = void 0;
36
+ const React = __importStar(require("react"));
37
+ const react_native_1 = require("react-native");
38
+ const AMAProvider_1 = require("../components/AMAProvider");
39
+ const useTimedAction = () => {
40
+ const { isScreenReaderEnabled } = (0, AMAProvider_1.useAMAContext)();
41
+ const onTimeout = React.useCallback((callback, milliseconds) => __awaiter(void 0, void 0, void 0, function* () {
42
+ if (isScreenReaderEnabled && react_native_1.Platform.OS === 'ios') {
43
+ return new Promise(resolve => {
44
+ resolve(null);
45
+ });
46
+ }
47
+ const timeout = yield getRecommendedTimeoutMillis(milliseconds);
48
+ return setTimeout(callback, timeout);
49
+ }), [isScreenReaderEnabled]);
50
+ return {
51
+ onTimeout,
52
+ };
53
+ };
54
+ exports.useTimedAction = useTimedAction;
55
+ function getRecommendedTimeoutMillis(milliseconds) {
56
+ return __awaiter(this, void 0, void 0, function* () {
57
+ return react_native_1.Platform.OS === 'android'
58
+ ? yield react_native_1.AccessibilityInfo.getRecommendedTimeoutMillis(milliseconds)
59
+ : new Promise(resolve => {
60
+ resolve(milliseconds);
61
+ });
62
+ });
63
+ }
@@ -0,0 +1,40 @@
1
+ import * as React from 'react';
2
+ import { AccessibilityInfo, Platform } from 'react-native';
3
+
4
+ import { useAMAContext } from '../components/AMAProvider';
5
+
6
+ export const useTimedAction = () => {
7
+ const { isScreenReaderEnabled } = useAMAContext();
8
+
9
+ const onTimeout = React.useCallback(
10
+ async (
11
+ callback: () => void,
12
+ milliseconds: number,
13
+ ): Promise<null | ReturnType<typeof setTimeout>> => {
14
+ if (isScreenReaderEnabled && Platform.OS === 'ios') {
15
+ return new Promise<null>(resolve => {
16
+ resolve(null);
17
+ });
18
+ }
19
+
20
+ const timeout = await getRecommendedTimeoutMillis(milliseconds);
21
+
22
+ return setTimeout(callback, timeout);
23
+ },
24
+ [isScreenReaderEnabled],
25
+ );
26
+
27
+ return {
28
+ onTimeout,
29
+ };
30
+ };
31
+
32
+ async function getRecommendedTimeoutMillis(
33
+ milliseconds: number,
34
+ ): Promise<number> {
35
+ return Platform.OS === 'android'
36
+ ? await AccessibilityInfo.getRecommendedTimeoutMillis(milliseconds)
37
+ : new Promise(resolve => {
38
+ resolve(milliseconds);
39
+ });
40
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export { AutofocusContainer } from './components/AutofocusContainer';
2
+ export { AMAProvider, useAMAContext, type AMAContextValue, type AMADevContextValue, type AMAProdContextValue, } from './components/AMAProvider';
3
+ export { HideChildrenFromAccessibilityTree } from './components/HideChildrenFromAccessibilityTree';
4
+ export { useButtonChecks } from './hooks/useButtonChecks';
5
+ export { useChecks } from './hooks/useChecks';
6
+ export { useFocus } from './hooks/useFocus';
7
+ export { useTimedAction } from './hooks/useTimedAction';
package/src/index.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useTimedAction = exports.useFocus = exports.useChecks = exports.useButtonChecks = exports.HideChildrenFromAccessibilityTree = exports.useAMAContext = exports.AMAProvider = exports.AutofocusContainer = void 0;
4
+ // Components
5
+ var AutofocusContainer_1 = require("./components/AutofocusContainer");
6
+ Object.defineProperty(exports, "AutofocusContainer", { enumerable: true, get: function () { return AutofocusContainer_1.AutofocusContainer; } });
7
+ var AMAProvider_1 = require("./components/AMAProvider");
8
+ Object.defineProperty(exports, "AMAProvider", { enumerable: true, get: function () { return AMAProvider_1.AMAProvider; } });
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
+ // Hooks
13
+ var useButtonChecks_1 = require("./hooks/useButtonChecks");
14
+ Object.defineProperty(exports, "useButtonChecks", { enumerable: true, get: function () { return useButtonChecks_1.useButtonChecks; } });
15
+ var useChecks_1 = require("./hooks/useChecks");
16
+ Object.defineProperty(exports, "useChecks", { enumerable: true, get: function () { return useChecks_1.useChecks; } });
17
+ var useFocus_1 = require("./hooks/useFocus");
18
+ Object.defineProperty(exports, "useFocus", { enumerable: true, get: function () { return useFocus_1.useFocus; } });
19
+ var useTimedAction_1 = require("./hooks/useTimedAction");
20
+ Object.defineProperty(exports, "useTimedAction", { enumerable: true, get: function () { return useTimedAction_1.useTimedAction; } });
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ // Components
2
+ export { AutofocusContainer } from './components/AutofocusContainer';
3
+ export {
4
+ AMAProvider,
5
+ useAMAContext,
6
+ type AMAContextValue,
7
+ type AMADevContextValue,
8
+ type AMAProdContextValue,
9
+ } from './components/AMAProvider';
10
+ export { HideChildrenFromAccessibilityTree } from './components/HideChildrenFromAccessibilityTree';
11
+
12
+ // Hooks
13
+ export { useButtonChecks } from './hooks/useButtonChecks';
14
+ export { useChecks } from './hooks/useChecks';
15
+ export { useFocus } from './hooks/useFocus';
16
+ export { useTimedAction } from './hooks/useTimedAction';
@@ -0,0 +1,11 @@
1
+ import { numerify } from './numerify';
2
+
3
+ beforeEach(() => {
4
+ jest.clearAllMocks();
5
+ });
6
+
7
+ describe('numerify', () => {
8
+ it('adds a space between each digit', () => {
9
+ expect(numerify('123')).toEqual('1 2 3');
10
+ });
11
+ });
@@ -0,0 +1,3 @@
1
+ export const numerify = (text: string) => {
2
+ return text.split('').join(' ');
3
+ };
@@ -1,3 +0,0 @@
1
- import type { AccessibilityRole } from 'react-native';
2
- import type { LogParams } from '../utils/logger';
3
- export declare const checkAccessibilityRole: (accessibilityRole: AccessibilityRole) => LogParams | null;