@react-native-ama/core 0.0.1 → 0.0.2

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.
@@ -0,0 +1,98 @@
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
+ };
96
+ }
97
+
98
+ 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,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,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.ts ADDED
@@ -0,0 +1,16 @@
1
+ // Components
2
+ export { AutofocusContainer } from './components/AutofocusContainer';
3
+ export { HideChildrenFromAccessibilityTree } from './components/HideChildrenFromAccessibilityTree';
4
+ export {
5
+ AMAProvider,
6
+ useAMAContext,
7
+ type AMAContextValue,
8
+ type AMADevContextValue,
9
+ type AMAProdContextValue,
10
+ } from './components/AMAProvider';
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
+ };