react-native-toast-message 2.1.3 → 2.2.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/lib/src/Toast.d.ts +1 -1
- package/lib/src/Toast.js +5 -3
- package/lib/src/ToastUI.js +1 -1
- package/lib/src/__helpers__/PanResponder.d.ts +3 -0
- package/lib/src/__helpers__/PanResponder.js +26 -0
- package/lib/src/__helpers__/testing-library/react-hooks.d.ts +9 -0
- package/lib/src/__helpers__/testing-library/react-hooks.js +13 -0
- package/lib/src/__helpers__/testing-library/react-native.d.ts +9 -0
- package/lib/src/__helpers__/testing-library/react-native.js +13 -0
- package/lib/src/__mocks__/EnvironmentProviderMock.d.ts +7 -0
- package/lib/src/__mocks__/EnvironmentProviderMock.js +9 -0
- package/lib/src/__tests__/Toast.test.d.ts +1 -0
- package/lib/src/__tests__/Toast.test.js +190 -0
- package/lib/src/__tests__/ToastUI.test.d.ts +1 -0
- package/lib/src/__tests__/ToastUI.test.js +52 -0
- package/lib/src/__tests__/useToast.test.d.ts +1 -0
- package/lib/src/__tests__/useToast.test.js +142 -0
- package/lib/src/components/AnimatedContainer.js +2 -2
- package/lib/src/components/BaseToast.js +2 -2
- package/lib/src/components/__tests__/AnimatedContainer.test.d.ts +1 -0
- package/lib/src/components/__tests__/AnimatedContainer.test.js +147 -0
- package/lib/src/components/__tests__/BaseToast.test.d.ts +1 -0
- package/lib/src/components/__tests__/BaseToast.test.js +81 -0
- package/lib/src/components/__tests__/ErrorToast.test.d.ts +1 -0
- package/lib/src/components/__tests__/ErrorToast.test.js +20 -0
- package/lib/src/components/__tests__/InfoToast.test.d.ts +1 -0
- package/lib/src/components/__tests__/InfoToast.test.js +20 -0
- package/lib/src/components/__tests__/SuccessToast.test.d.ts +1 -0
- package/lib/src/components/__tests__/SuccessToast.test.js +20 -0
- package/lib/src/contexts/EnvironmentContext.d.ts +8 -0
- package/lib/src/contexts/EnvironmentContext.js +22 -0
- package/lib/src/contexts/__tests__/EnvironmentContext.test.d.ts +1 -0
- package/lib/src/contexts/__tests__/EnvironmentContext.test.js +29 -0
- package/lib/src/contexts/__tests__/LoggerContext.test.d.ts +1 -0
- package/lib/src/contexts/__tests__/LoggerContext.test.js +35 -0
- package/lib/src/contexts/index.d.ts +4 -1
- package/lib/src/contexts/index.js +2 -1
- package/lib/src/hooks/__tests__/useKeyboard.test.d.ts +1 -0
- package/lib/src/hooks/__tests__/useKeyboard.test.js +60 -0
- package/lib/src/hooks/__tests__/usePanResponder.test.d.ts +1 -0
- package/lib/src/hooks/__tests__/usePanResponder.test.js +112 -0
- package/lib/src/hooks/__tests__/useSlideAnimation.test.d.ts +1 -0
- package/lib/src/hooks/__tests__/useSlideAnimation.test.js +88 -0
- package/lib/src/hooks/__tests__/useTimeout.test.d.ts +1 -0
- package/lib/src/hooks/__tests__/useTimeout.test.js +54 -0
- package/lib/src/hooks/__tests__/useViewDimensions.test.d.ts +1 -0
- package/lib/src/hooks/__tests__/useViewDimensions.test.js +51 -0
- package/lib/src/hooks/useSlideAnimation.d.ts +6 -2
- package/lib/src/hooks/useSlideAnimation.js +37 -12
- package/lib/src/types/index.d.ts +9 -0
- package/lib/src/utils/__tests__/number.test.d.ts +1 -0
- package/lib/src/utils/__tests__/number.test.js +15 -0
- package/lib/src/utils/__tests__/obj.test.d.ts +1 -0
- package/lib/src/utils/__tests__/obj.test.js +21 -0
- package/package.json +21 -11
- package/CHANGELOG.md +0 -299
- package/lib/src/utils/array.d.ts +0 -2
- package/lib/src/utils/array.js +0 -4
package/lib/src/Toast.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { ToastProps, ToastShowParams } from './types';
|
|
3
|
-
export declare function Toast(props: ToastProps): JSX.Element;
|
|
3
|
+
export declare function Toast({ useNativeDriver, ...props }: ToastProps): JSX.Element;
|
|
4
4
|
export declare namespace Toast {
|
|
5
5
|
var show: (params: ToastShowParams) => void;
|
|
6
6
|
var hide: (params?: void | undefined) => void;
|
package/lib/src/Toast.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { LoggerProvider } from './contexts';
|
|
2
|
+
import { EnvironmentProvider, LoggerProvider } from './contexts';
|
|
3
3
|
import { ToastUI } from './ToastUI';
|
|
4
4
|
import { useToast } from './useToast';
|
|
5
5
|
const ToastRoot = React.forwardRef((props, ref) => {
|
|
@@ -33,7 +33,7 @@ function addNewRef(newRef) {
|
|
|
33
33
|
function removeOldRef(oldRef) {
|
|
34
34
|
refs = refs.filter((r) => r.current !== oldRef);
|
|
35
35
|
}
|
|
36
|
-
export function Toast(props) {
|
|
36
|
+
export function Toast({ useNativeDriver, ...props }) {
|
|
37
37
|
const toastRef = React.useRef(null);
|
|
38
38
|
/*
|
|
39
39
|
This must use `useCallback` to ensure the ref doesn't get set to null and then a new ref every render.
|
|
@@ -53,7 +53,9 @@ export function Toast(props) {
|
|
|
53
53
|
}
|
|
54
54
|
}, []);
|
|
55
55
|
return (<LoggerProvider enableLogs={false}>
|
|
56
|
-
<
|
|
56
|
+
<EnvironmentProvider useNativeDriver={useNativeDriver}>
|
|
57
|
+
<ToastRoot ref={setRef} {...props}/>
|
|
58
|
+
</EnvironmentProvider>
|
|
57
59
|
</LoggerProvider>);
|
|
58
60
|
}
|
|
59
61
|
/**
|
package/lib/src/ToastUI.js
CHANGED
|
@@ -17,7 +17,7 @@ function renderComponent({ data, options, config, isVisible, show, hide }) {
|
|
|
17
17
|
};
|
|
18
18
|
const ToastComponent = toastConfig[type];
|
|
19
19
|
if (!ToastComponent) {
|
|
20
|
-
throw new Error(`Toast type: '${type}' does not exist. You can add it via the 'config' prop on the Toast instance. Learn more: https://github.com/calintamas/react-native-toast-message/blob/
|
|
20
|
+
throw new Error(`Toast type: '${type}' does not exist. You can add it via the 'config' prop on the Toast instance. Learn more: https://github.com/calintamas/react-native-toast-message/blob/HEAD/docs/custom-layouts.md`);
|
|
21
21
|
}
|
|
22
22
|
return ToastComponent({
|
|
23
23
|
position,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PanResponder } from 'react-native';
|
|
2
|
+
export const mockGestureValues = {
|
|
3
|
+
moveY: 0,
|
|
4
|
+
moveX: 0,
|
|
5
|
+
y0: 0,
|
|
6
|
+
x0: 0,
|
|
7
|
+
dx: 0,
|
|
8
|
+
dy: 10,
|
|
9
|
+
stateID: 123,
|
|
10
|
+
vx: 0,
|
|
11
|
+
vy: 0,
|
|
12
|
+
numberActiveTouches: 1,
|
|
13
|
+
_accountsForMovesUpTo: 1
|
|
14
|
+
};
|
|
15
|
+
export function mockPanResponder() {
|
|
16
|
+
jest
|
|
17
|
+
.spyOn(PanResponder, 'create')
|
|
18
|
+
.mockImplementation(({ onMoveShouldSetPanResponder, onMoveShouldSetPanResponderCapture, onPanResponderMove, onPanResponderRelease }) => ({
|
|
19
|
+
panHandlers: {
|
|
20
|
+
onMoveShouldSetResponder: onMoveShouldSetPanResponder,
|
|
21
|
+
onMoveShouldSetResponderCapture: onMoveShouldSetPanResponderCapture,
|
|
22
|
+
onResponderMove: onPanResponderMove,
|
|
23
|
+
onResponderRelease: onPanResponderRelease
|
|
24
|
+
}
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { renderHook as renderHookFn } from '@testing-library/react-hooks';
|
|
2
|
+
/**
|
|
3
|
+
* Custom `renderHook` function implementation to be able to easily wrap test environment providers
|
|
4
|
+
*
|
|
5
|
+
* https://testing-library.com/docs/react-testing-library/setup/#custom-render
|
|
6
|
+
*/
|
|
7
|
+
export declare const renderHook: typeof renderHookFn;
|
|
8
|
+
export { act } from '@testing-library/react-hooks';
|
|
9
|
+
export type { RenderResult } from '@testing-library/react-hooks';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/* eslint-disable no-restricted-imports */
|
|
2
|
+
import { renderHook as renderHookFn } from '@testing-library/react-hooks';
|
|
3
|
+
import { EnvironmentProviderMock } from '~/__mocks__/EnvironmentProviderMock';
|
|
4
|
+
/**
|
|
5
|
+
* Custom `renderHook` function implementation to be able to easily wrap test environment providers
|
|
6
|
+
*
|
|
7
|
+
* https://testing-library.com/docs/react-testing-library/setup/#custom-render
|
|
8
|
+
*/
|
|
9
|
+
export const renderHook = (cb, options) => renderHookFn(cb, {
|
|
10
|
+
wrapper: EnvironmentProviderMock,
|
|
11
|
+
...options
|
|
12
|
+
});
|
|
13
|
+
export { act } from '@testing-library/react-hooks';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { render as renderFn } from '@testing-library/react-native';
|
|
2
|
+
/**
|
|
3
|
+
* Custom `render` function implementation to be able to easily wrap test environment providers
|
|
4
|
+
*
|
|
5
|
+
* https://testing-library.com/docs/react-testing-library/setup/#custom-render
|
|
6
|
+
*/
|
|
7
|
+
export declare const render: typeof renderFn;
|
|
8
|
+
export { waitFor, fireEvent, act, cleanup } from '@testing-library/react-native';
|
|
9
|
+
export type { RenderOptions, RenderAPI, GetByAPI } from '@testing-library/react-native';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/* eslint-disable no-restricted-imports */
|
|
2
|
+
import { render as renderFn } from '@testing-library/react-native';
|
|
3
|
+
import { EnvironmentProviderMock } from '../../__mocks__/EnvironmentProviderMock';
|
|
4
|
+
/**
|
|
5
|
+
* Custom `render` function implementation to be able to easily wrap test environment providers
|
|
6
|
+
*
|
|
7
|
+
* https://testing-library.com/docs/react-testing-library/setup/#custom-render
|
|
8
|
+
*/
|
|
9
|
+
export const render = (component, options) => renderFn(component, {
|
|
10
|
+
wrapper: EnvironmentProviderMock,
|
|
11
|
+
...options
|
|
12
|
+
});
|
|
13
|
+
export { waitFor, fireEvent, act, cleanup } from '@testing-library/react-native';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { EnvironmentContextType } from '../contexts';
|
|
3
|
+
export declare const environmentMock: EnvironmentContextType;
|
|
4
|
+
/**
|
|
5
|
+
* Provides test implementations for injected dependencies
|
|
6
|
+
*/
|
|
7
|
+
export declare const EnvironmentProviderMock: React.FC;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { EnvironmentProvider } from '../contexts';
|
|
3
|
+
export const environmentMock = {
|
|
4
|
+
useNativeDriver: false
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Provides test implementations for injected dependencies
|
|
8
|
+
*/
|
|
9
|
+
export const EnvironmentProviderMock = ({ children }) => (<EnvironmentProvider {...environmentMock}>{children}</EnvironmentProvider>);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/* eslint-env jest */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Button, Modal, Text } from 'react-native';
|
|
4
|
+
import { act, fireEvent, render, waitFor } from '~/__helpers__/testing-library/react-native';
|
|
5
|
+
import { Toast } from '../Toast';
|
|
6
|
+
/*
|
|
7
|
+
The Modal component is automatically mocked by RN and apparently contains a bug which makes the Modal
|
|
8
|
+
(and its children) to always be visible in the test tree.
|
|
9
|
+
|
|
10
|
+
This fixes the issue:
|
|
11
|
+
*/
|
|
12
|
+
jest.mock('react-native/Libraries/Modal/Modal', () => {
|
|
13
|
+
const ActualModal = jest.requireActual('react-native/Libraries/Modal/Modal');
|
|
14
|
+
return (props) => <ActualModal {...props}/>;
|
|
15
|
+
});
|
|
16
|
+
jest.mock('react-native/Libraries/LogBox/LogBox');
|
|
17
|
+
describe('test Toast component', () => {
|
|
18
|
+
it('creates imperative handle', () => {
|
|
19
|
+
const onShow = jest.fn();
|
|
20
|
+
const onHide = jest.fn();
|
|
21
|
+
render(<Toast onShow={onShow} onHide={onHide}/>);
|
|
22
|
+
expect(Toast.show).toBeDefined();
|
|
23
|
+
expect(Toast.hide).toBeDefined();
|
|
24
|
+
act(() => {
|
|
25
|
+
Toast.show({
|
|
26
|
+
text1: 'test'
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
expect(onShow).toHaveBeenCalled();
|
|
30
|
+
act(() => {
|
|
31
|
+
Toast.hide();
|
|
32
|
+
});
|
|
33
|
+
expect(onHide).toHaveBeenCalled();
|
|
34
|
+
});
|
|
35
|
+
it('shows Toast inside a Modal', async () => {
|
|
36
|
+
const onShow = jest.fn();
|
|
37
|
+
const onHide = jest.fn();
|
|
38
|
+
const onShowInsideModal = jest.fn();
|
|
39
|
+
const onHideInsideModal = jest.fn();
|
|
40
|
+
const ModalWrapper = () => {
|
|
41
|
+
const [isVisible, setIsVisible] = React.useState(false);
|
|
42
|
+
return (<>
|
|
43
|
+
<Toast onShow={onShow} onHide={onHide}/>
|
|
44
|
+
<Text>Outside modal</Text>
|
|
45
|
+
<Button title='Show modal' onPress={() => setIsVisible(true)}/>
|
|
46
|
+
<Modal visible={isVisible}>
|
|
47
|
+
<Text>Inside modal</Text>
|
|
48
|
+
<Button title='Hide modal' onPress={() => setIsVisible(false)}/>
|
|
49
|
+
<Toast onShow={onShowInsideModal} onHide={onHideInsideModal}/>
|
|
50
|
+
</Modal>
|
|
51
|
+
</>);
|
|
52
|
+
};
|
|
53
|
+
const utils = render(<ModalWrapper />);
|
|
54
|
+
expect(utils.queryByText('Outside modal')).toBeTruthy();
|
|
55
|
+
expect(Toast.show).toBeDefined();
|
|
56
|
+
expect(Toast.hide).toBeDefined();
|
|
57
|
+
// Test the Toast instance that's outside the Modal
|
|
58
|
+
act(() => {
|
|
59
|
+
Toast.show({
|
|
60
|
+
text1: 'test'
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
expect(onShow).toHaveBeenCalled();
|
|
64
|
+
act(() => {
|
|
65
|
+
Toast.hide();
|
|
66
|
+
});
|
|
67
|
+
expect(onHide).toHaveBeenCalled();
|
|
68
|
+
// Show the Modal
|
|
69
|
+
const showModalButton = utils.getByText('Show modal');
|
|
70
|
+
expect(showModalButton).toBeTruthy();
|
|
71
|
+
fireEvent.press(showModalButton);
|
|
72
|
+
await waitFor(() => {
|
|
73
|
+
expect(utils.queryByText('Inside modal')).toBeTruthy();
|
|
74
|
+
});
|
|
75
|
+
// Test the Toast instance that's inside the Modal
|
|
76
|
+
act(() => {
|
|
77
|
+
Toast.show({
|
|
78
|
+
text1: 'test'
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
expect(onShowInsideModal).toHaveBeenCalled();
|
|
82
|
+
act(() => {
|
|
83
|
+
Toast.hide();
|
|
84
|
+
});
|
|
85
|
+
expect(onHideInsideModal).toHaveBeenCalled();
|
|
86
|
+
// Hide modal
|
|
87
|
+
const hideModalButton = utils.getByText('Hide modal');
|
|
88
|
+
expect(hideModalButton).toBeTruthy();
|
|
89
|
+
fireEvent.press(hideModalButton);
|
|
90
|
+
await waitFor(() => {
|
|
91
|
+
expect(utils.queryByText('Inside modal')).toBeFalsy();
|
|
92
|
+
});
|
|
93
|
+
// Now that the Modal is hidden, the the outside Toast instance again
|
|
94
|
+
act(() => {
|
|
95
|
+
Toast.show({
|
|
96
|
+
text1: 'test'
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
expect(onShow).toHaveBeenCalledTimes(2);
|
|
100
|
+
expect(onShowInsideModal).toHaveBeenCalledTimes(1);
|
|
101
|
+
act(() => {
|
|
102
|
+
Toast.hide();
|
|
103
|
+
});
|
|
104
|
+
expect(onHide).toHaveBeenCalledTimes(2);
|
|
105
|
+
expect(onHideInsideModal).toHaveBeenCalledTimes(1);
|
|
106
|
+
});
|
|
107
|
+
it(`doesn't show a Toast if there is no active ref`, () => {
|
|
108
|
+
const onShow = jest.fn();
|
|
109
|
+
const onHide = jest.fn();
|
|
110
|
+
const ModalWrapper = () => (<>
|
|
111
|
+
<Modal visible={false}>
|
|
112
|
+
<Text>Inside modal</Text>
|
|
113
|
+
<Toast onShow={onShow} onHide={onHide}/>
|
|
114
|
+
</Modal>
|
|
115
|
+
</>);
|
|
116
|
+
render(<ModalWrapper />);
|
|
117
|
+
act(() => {
|
|
118
|
+
Toast.show({
|
|
119
|
+
text1: 'test'
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
expect(onShow).not.toHaveBeenCalled();
|
|
123
|
+
});
|
|
124
|
+
it('when autoHide: true, the Toast only hides when timer reaches visibilityTime', () => {
|
|
125
|
+
jest.useFakeTimers();
|
|
126
|
+
const onShow = jest.fn();
|
|
127
|
+
const onHide = jest.fn();
|
|
128
|
+
const visibilityTime = 1500;
|
|
129
|
+
render(<Toast onShow={onShow} onHide={onHide} autoHide={true} visibilityTime={visibilityTime}/>);
|
|
130
|
+
act(() => {
|
|
131
|
+
Toast.show({
|
|
132
|
+
text1: 'I am auto hiding, by default'
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
expect(onShow).toHaveBeenCalled();
|
|
136
|
+
// Advance time by 1/4 visibilityTime
|
|
137
|
+
// Toast should not hide yet
|
|
138
|
+
jest.advanceTimersByTime(visibilityTime / 4);
|
|
139
|
+
expect(onHide).not.toHaveBeenCalled();
|
|
140
|
+
// Do it again 1/4 visibilityTime
|
|
141
|
+
// Toast should still be visible
|
|
142
|
+
jest.advanceTimersByTime(visibilityTime / 4);
|
|
143
|
+
expect(onHide).not.toHaveBeenCalled();
|
|
144
|
+
// Advance time by the remaining 1/2 visibilityTime
|
|
145
|
+
act(() => {
|
|
146
|
+
jest.advanceTimersByTime(visibilityTime / 2);
|
|
147
|
+
});
|
|
148
|
+
// Now the Toast should hide
|
|
149
|
+
expect(onHide).toHaveBeenCalled();
|
|
150
|
+
jest.useRealTimers();
|
|
151
|
+
});
|
|
152
|
+
it('clears previous autoHide: true timer when a new Toast is presented with autoHide: false', () => {
|
|
153
|
+
jest.useFakeTimers();
|
|
154
|
+
const onShow = jest.fn();
|
|
155
|
+
const onHide = jest.fn();
|
|
156
|
+
const visibilityTime = 1500;
|
|
157
|
+
render(<Toast onShow={onShow} onHide={onHide} autoHide={true} visibilityTime={visibilityTime}/>);
|
|
158
|
+
act(() => {
|
|
159
|
+
Toast.show({
|
|
160
|
+
text1: 'I am auto hiding, by default'
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
expect(onShow).toHaveBeenCalled();
|
|
164
|
+
// Advance by 1/2 visibilityTime
|
|
165
|
+
// Toast should not hide
|
|
166
|
+
jest.advanceTimersByTime(visibilityTime / 2);
|
|
167
|
+
expect(onHide).not.toHaveBeenCalled();
|
|
168
|
+
// Now, show another Toast, before the previous one has the chance to hide
|
|
169
|
+
act(() => {
|
|
170
|
+
Toast.show({
|
|
171
|
+
text1: 'I am NOT auto hiding',
|
|
172
|
+
autoHide: false
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
// Advance time to the end of visibilityTime
|
|
176
|
+
act(() => {
|
|
177
|
+
jest.advanceTimersByTime(visibilityTime / 2);
|
|
178
|
+
});
|
|
179
|
+
// The new Toast should NOT hide
|
|
180
|
+
// (the previous timer should have been cleared before reaching the end)
|
|
181
|
+
expect(onHide).not.toHaveBeenCalled();
|
|
182
|
+
// And even go beyond visibilityTime
|
|
183
|
+
act(() => {
|
|
184
|
+
jest.advanceTimersByTime(1000);
|
|
185
|
+
});
|
|
186
|
+
// The new Toast should still not hide
|
|
187
|
+
expect(onHide).not.toHaveBeenCalled();
|
|
188
|
+
jest.useRealTimers();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/* eslint-env jest */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from '~/__helpers__/testing-library/react-native';
|
|
4
|
+
import { ToastUI } from '../ToastUI';
|
|
5
|
+
import { DEFAULT_OPTIONS } from '../useToast';
|
|
6
|
+
const setup = (props) => {
|
|
7
|
+
const show = jest.fn();
|
|
8
|
+
const hide = jest.fn();
|
|
9
|
+
const utils = render(<ToastUI isVisible options={DEFAULT_OPTIONS} data={{
|
|
10
|
+
text1: 'text1',
|
|
11
|
+
text2: 'text2'
|
|
12
|
+
}} show={show} hide={hide} {...props}/>);
|
|
13
|
+
return {
|
|
14
|
+
...utils
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
describe('test ToastUI component', () => {
|
|
18
|
+
it('renders defaults', () => {
|
|
19
|
+
const { queryByText } = setup();
|
|
20
|
+
expect(queryByText('text1')).toBeDefined();
|
|
21
|
+
expect(queryByText('text2')).toBeDefined();
|
|
22
|
+
});
|
|
23
|
+
it('renders error type Toast', () => {
|
|
24
|
+
const { queryByText } = setup({
|
|
25
|
+
options: {
|
|
26
|
+
...DEFAULT_OPTIONS,
|
|
27
|
+
type: 'error'
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
expect(queryByText('text1')).toBeDefined();
|
|
31
|
+
expect(queryByText('text2')).toBeDefined();
|
|
32
|
+
});
|
|
33
|
+
it('renders info type Toast', () => {
|
|
34
|
+
const { queryByText } = setup({
|
|
35
|
+
options: {
|
|
36
|
+
...DEFAULT_OPTIONS,
|
|
37
|
+
type: 'info'
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
expect(queryByText('text1')).toBeDefined();
|
|
41
|
+
expect(queryByText('text2')).toBeDefined();
|
|
42
|
+
});
|
|
43
|
+
it('throws when trying to render an undefined Toast type', () => {
|
|
44
|
+
const type = 'mock';
|
|
45
|
+
expect(() => setup({
|
|
46
|
+
options: {
|
|
47
|
+
...DEFAULT_OPTIONS,
|
|
48
|
+
type
|
|
49
|
+
}
|
|
50
|
+
})).toThrow(new Error(`Toast type: '${type}' does not exist. You can add it via the 'config' prop on the Toast instance. Learn more: https://github.com/calintamas/react-native-toast-message/blob/HEAD/docs/custom-layouts.md`));
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/* eslint-env jest */
|
|
2
|
+
import { act, renderHook } from '~/__helpers__/testing-library/react-hooks';
|
|
3
|
+
import { DEFAULT_DATA, DEFAULT_OPTIONS, useToast } from '../useToast';
|
|
4
|
+
const setup = () => {
|
|
5
|
+
const utils = renderHook(() => useToast({
|
|
6
|
+
defaultOptions: DEFAULT_OPTIONS
|
|
7
|
+
}));
|
|
8
|
+
return {
|
|
9
|
+
...utils
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
describe('test useToast hook', () => {
|
|
13
|
+
it('returns defaults', () => {
|
|
14
|
+
const { result } = setup();
|
|
15
|
+
const { isVisible, data, options, show, hide } = result.current;
|
|
16
|
+
expect(isVisible).toBe(false);
|
|
17
|
+
expect(data).toEqual(DEFAULT_DATA);
|
|
18
|
+
expect(options).toEqual(DEFAULT_OPTIONS);
|
|
19
|
+
expect(show).toBeDefined();
|
|
20
|
+
expect(hide).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
it('set isVisible: true when show is called', () => {
|
|
23
|
+
const { result } = setup();
|
|
24
|
+
act(() => {
|
|
25
|
+
result.current.show({
|
|
26
|
+
text1: 'test'
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
expect(result.current.isVisible).toBe(true);
|
|
30
|
+
expect(result.current.data.text1).toBe('test');
|
|
31
|
+
});
|
|
32
|
+
it('calls onShow when Toast is shown', () => {
|
|
33
|
+
const { result } = setup();
|
|
34
|
+
const onShow = jest.fn();
|
|
35
|
+
act(() => {
|
|
36
|
+
result.current.show({
|
|
37
|
+
text1: 'test',
|
|
38
|
+
onShow
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
expect(result.current.isVisible).toBe(true);
|
|
42
|
+
expect(result.current.data.text1).toBe('test');
|
|
43
|
+
expect(onShow).toHaveBeenCalled();
|
|
44
|
+
});
|
|
45
|
+
it('set isVisible: false when hide is called', () => {
|
|
46
|
+
const { result } = setup();
|
|
47
|
+
act(() => {
|
|
48
|
+
result.current.show({
|
|
49
|
+
text1: 'test'
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
expect(result.current.isVisible).toBe(true);
|
|
53
|
+
expect(result.current.data.text1).toBe('test');
|
|
54
|
+
act(() => {
|
|
55
|
+
result.current.hide();
|
|
56
|
+
});
|
|
57
|
+
expect(result.current.isVisible).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
it('calls onHide when Toast is shown', () => {
|
|
60
|
+
const { result } = setup();
|
|
61
|
+
const onHide = jest.fn();
|
|
62
|
+
act(() => {
|
|
63
|
+
result.current.show({
|
|
64
|
+
text1: 'test',
|
|
65
|
+
onHide
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
expect(result.current.isVisible).toBe(true);
|
|
69
|
+
expect(result.current.data.text1).toBe('test');
|
|
70
|
+
act(() => {
|
|
71
|
+
result.current.hide();
|
|
72
|
+
});
|
|
73
|
+
expect(result.current.isVisible).toBe(false);
|
|
74
|
+
expect(onHide).toHaveBeenCalled();
|
|
75
|
+
});
|
|
76
|
+
it('sets data values on show', () => {
|
|
77
|
+
const { result } = setup();
|
|
78
|
+
act(() => {
|
|
79
|
+
result.current.show({
|
|
80
|
+
text1: 'text1',
|
|
81
|
+
text2: 'text2'
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
expect(result.current.isVisible).toBe(true);
|
|
85
|
+
expect(result.current.data.text1).toBe('text1');
|
|
86
|
+
expect(result.current.data.text2).toBe('text2');
|
|
87
|
+
});
|
|
88
|
+
it('sets option values on show', () => {
|
|
89
|
+
const { result } = setup();
|
|
90
|
+
const options = {
|
|
91
|
+
type: 'info',
|
|
92
|
+
position: 'bottom',
|
|
93
|
+
autoHide: false,
|
|
94
|
+
visibilityTime: 20,
|
|
95
|
+
topOffset: 120,
|
|
96
|
+
bottomOffset: 130,
|
|
97
|
+
keyboardOffset: 5,
|
|
98
|
+
onShow: jest.fn(),
|
|
99
|
+
onHide: jest.fn(),
|
|
100
|
+
onPress: jest.fn(),
|
|
101
|
+
props: {
|
|
102
|
+
foo: 'bar'
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
act(() => {
|
|
106
|
+
result.current.show({
|
|
107
|
+
text1: 'test',
|
|
108
|
+
...options
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
expect(result.current.isVisible).toBe(true);
|
|
112
|
+
expect(result.current.options).toEqual(options);
|
|
113
|
+
});
|
|
114
|
+
it('automatically hides when autoHide: true', () => {
|
|
115
|
+
jest.useFakeTimers();
|
|
116
|
+
const { result } = setup();
|
|
117
|
+
const onHide = jest.fn();
|
|
118
|
+
act(() => {
|
|
119
|
+
result.current.show({
|
|
120
|
+
text1: 'test',
|
|
121
|
+
autoHide: true,
|
|
122
|
+
onHide
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
expect(result.current.isVisible).toBe(true);
|
|
126
|
+
act(() => {
|
|
127
|
+
jest.runAllTimers();
|
|
128
|
+
});
|
|
129
|
+
expect(result.current.isVisible).toBe(false);
|
|
130
|
+
expect(onHide).toHaveBeenCalled();
|
|
131
|
+
});
|
|
132
|
+
it('shows using only text2', () => {
|
|
133
|
+
const { result } = setup();
|
|
134
|
+
act(() => {
|
|
135
|
+
result.current.show({
|
|
136
|
+
text2: 'text2'
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
expect(result.current.isVisible).toBe(true);
|
|
140
|
+
expect(result.current.data.text2).toBe('text2');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -40,7 +40,7 @@ export function animatedValueFor(gesture, position, damping) {
|
|
|
40
40
|
export function AnimatedContainer({ children, isVisible, position, topOffset, bottomOffset, keyboardOffset, onHide, onRestorePosition = noop }) {
|
|
41
41
|
const { log } = useLogger();
|
|
42
42
|
const { computeViewDimensions, height } = useViewDimensions();
|
|
43
|
-
const { animatedValue, animate, animationStyles } = useSlideAnimation({
|
|
43
|
+
const { animatedValue, animate, defaultStyles, animationStyles } = useSlideAnimation({
|
|
44
44
|
position,
|
|
45
45
|
height,
|
|
46
46
|
topOffset,
|
|
@@ -72,7 +72,7 @@ export function AnimatedContainer({ children, isVisible, position, topOffset, bo
|
|
|
72
72
|
const newAnimationValue = isVisible ? 1 : 0;
|
|
73
73
|
animate(newAnimationValue);
|
|
74
74
|
}, [animate, isVisible]);
|
|
75
|
-
return (<Animated.View testID={getTestId('AnimatedContainer')} onLayout={computeViewDimensions} style={[styles.base, styles[position], animationStyles]}
|
|
75
|
+
return (<Animated.View testID={getTestId('AnimatedContainer')} onLayout={computeViewDimensions} style={[styles.base, styles[position], defaultStyles, animationStyles]}
|
|
76
76
|
// This container View is never the target of touch events but its subviews can be.
|
|
77
77
|
// By doing this, tapping buttons behind the Toast is allowed
|
|
78
78
|
pointerEvents='box-none' {...panResponder.panHandlers}>
|
|
@@ -7,10 +7,10 @@ export function BaseToast({ text1, text2, onPress, activeOpacity, style, touchab
|
|
|
7
7
|
return (<Touchable testID={getTestId('TouchableContainer')} onPress={onPress} activeOpacity={activeOpacity} style={[styles.base, styles.leadingBorder, style]} {...touchableContainerProps}>
|
|
8
8
|
{renderLeadingIcon && renderLeadingIcon()}
|
|
9
9
|
<View testID={getTestId('ContentContainer')} style={[styles.contentContainer, contentContainerStyle]} {...contentContainerProps}>
|
|
10
|
-
{text1
|
|
10
|
+
{(text1?.length ?? 0) > 0 && (<Text testID={getTestId('Text1')} style={[styles.text1, text1Style]} numberOfLines={text1NumberOfLines} ellipsizeMode='tail' {...text1Props}>
|
|
11
11
|
{text1}
|
|
12
12
|
</Text>)}
|
|
13
|
-
{text2
|
|
13
|
+
{(text2?.length ?? 0) > 0 && (<Text testID={getTestId('Text2')} style={[styles.text2, text2Style]} numberOfLines={text2NumberOfLines} ellipsizeMode='tail' {...text2Props}>
|
|
14
14
|
{text2}
|
|
15
15
|
</Text>)}
|
|
16
16
|
</View>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|