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.
Files changed (58) hide show
  1. package/lib/src/Toast.d.ts +1 -1
  2. package/lib/src/Toast.js +5 -3
  3. package/lib/src/ToastUI.js +1 -1
  4. package/lib/src/__helpers__/PanResponder.d.ts +3 -0
  5. package/lib/src/__helpers__/PanResponder.js +26 -0
  6. package/lib/src/__helpers__/testing-library/react-hooks.d.ts +9 -0
  7. package/lib/src/__helpers__/testing-library/react-hooks.js +13 -0
  8. package/lib/src/__helpers__/testing-library/react-native.d.ts +9 -0
  9. package/lib/src/__helpers__/testing-library/react-native.js +13 -0
  10. package/lib/src/__mocks__/EnvironmentProviderMock.d.ts +7 -0
  11. package/lib/src/__mocks__/EnvironmentProviderMock.js +9 -0
  12. package/lib/src/__tests__/Toast.test.d.ts +1 -0
  13. package/lib/src/__tests__/Toast.test.js +190 -0
  14. package/lib/src/__tests__/ToastUI.test.d.ts +1 -0
  15. package/lib/src/__tests__/ToastUI.test.js +52 -0
  16. package/lib/src/__tests__/useToast.test.d.ts +1 -0
  17. package/lib/src/__tests__/useToast.test.js +142 -0
  18. package/lib/src/components/AnimatedContainer.js +2 -2
  19. package/lib/src/components/BaseToast.js +2 -2
  20. package/lib/src/components/__tests__/AnimatedContainer.test.d.ts +1 -0
  21. package/lib/src/components/__tests__/AnimatedContainer.test.js +147 -0
  22. package/lib/src/components/__tests__/BaseToast.test.d.ts +1 -0
  23. package/lib/src/components/__tests__/BaseToast.test.js +81 -0
  24. package/lib/src/components/__tests__/ErrorToast.test.d.ts +1 -0
  25. package/lib/src/components/__tests__/ErrorToast.test.js +20 -0
  26. package/lib/src/components/__tests__/InfoToast.test.d.ts +1 -0
  27. package/lib/src/components/__tests__/InfoToast.test.js +20 -0
  28. package/lib/src/components/__tests__/SuccessToast.test.d.ts +1 -0
  29. package/lib/src/components/__tests__/SuccessToast.test.js +20 -0
  30. package/lib/src/contexts/EnvironmentContext.d.ts +8 -0
  31. package/lib/src/contexts/EnvironmentContext.js +22 -0
  32. package/lib/src/contexts/__tests__/EnvironmentContext.test.d.ts +1 -0
  33. package/lib/src/contexts/__tests__/EnvironmentContext.test.js +29 -0
  34. package/lib/src/contexts/__tests__/LoggerContext.test.d.ts +1 -0
  35. package/lib/src/contexts/__tests__/LoggerContext.test.js +35 -0
  36. package/lib/src/contexts/index.d.ts +4 -1
  37. package/lib/src/contexts/index.js +2 -1
  38. package/lib/src/hooks/__tests__/useKeyboard.test.d.ts +1 -0
  39. package/lib/src/hooks/__tests__/useKeyboard.test.js +60 -0
  40. package/lib/src/hooks/__tests__/usePanResponder.test.d.ts +1 -0
  41. package/lib/src/hooks/__tests__/usePanResponder.test.js +112 -0
  42. package/lib/src/hooks/__tests__/useSlideAnimation.test.d.ts +1 -0
  43. package/lib/src/hooks/__tests__/useSlideAnimation.test.js +88 -0
  44. package/lib/src/hooks/__tests__/useTimeout.test.d.ts +1 -0
  45. package/lib/src/hooks/__tests__/useTimeout.test.js +54 -0
  46. package/lib/src/hooks/__tests__/useViewDimensions.test.d.ts +1 -0
  47. package/lib/src/hooks/__tests__/useViewDimensions.test.js +51 -0
  48. package/lib/src/hooks/useSlideAnimation.d.ts +6 -2
  49. package/lib/src/hooks/useSlideAnimation.js +37 -12
  50. package/lib/src/types/index.d.ts +9 -0
  51. package/lib/src/utils/__tests__/number.test.d.ts +1 -0
  52. package/lib/src/utils/__tests__/number.test.js +15 -0
  53. package/lib/src/utils/__tests__/obj.test.d.ts +1 -0
  54. package/lib/src/utils/__tests__/obj.test.js +21 -0
  55. package/package.json +21 -11
  56. package/CHANGELOG.md +0 -299
  57. package/lib/src/utils/array.d.ts +0 -2
  58. package/lib/src/utils/array.js +0 -4
@@ -0,0 +1,88 @@
1
+ /* eslint-env jest */
2
+ import { Animated } from 'react-native';
3
+ import { renderHook } from '~/__helpers__/testing-library/react-hooks';
4
+ import { translateYOutputRangeFor, useSlideAnimation } from '../useSlideAnimation';
5
+ const defaultOffsets = {
6
+ topOffset: 40,
7
+ bottomOffset: 60,
8
+ keyboardOffset: 5
9
+ };
10
+ const setup = (props) => {
11
+ const utils = renderHook(() => useSlideAnimation({
12
+ position: 'top',
13
+ height: 20,
14
+ ...defaultOffsets,
15
+ ...props
16
+ }));
17
+ return {
18
+ ...utils
19
+ };
20
+ };
21
+ describe('test useSlideAnimation hook', () => {
22
+ it('returns defaults', () => {
23
+ const { result } = setup();
24
+ const { animatedValue, animate, animationStyles } = result.current;
25
+ expect(animatedValue.current).toBeDefined();
26
+ expect(animate).toBeDefined();
27
+ expect(animationStyles.opacity).toBeDefined();
28
+ expect(animationStyles.transform).toBeDefined();
29
+ });
30
+ it('animates to a new value', async () => {
31
+ const spy = jest.spyOn(Animated, 'spring').mockImplementation(() => ({
32
+ start: jest.fn(),
33
+ stop: jest.fn(),
34
+ reset: jest.fn()
35
+ }));
36
+ const { result } = setup();
37
+ result.current.animate(1);
38
+ expect(spy).toHaveBeenCalled();
39
+ });
40
+ it.each([
41
+ ['top', -40],
42
+ ['bottom', 40]
43
+ ])('returns default styles for position: %s', (position, translateY) => {
44
+ const { result } = setup({
45
+ position
46
+ });
47
+ expect(result.current.defaultStyles).toEqual({
48
+ transform: [
49
+ {
50
+ translateY
51
+ }
52
+ ]
53
+ });
54
+ });
55
+ });
56
+ describe('test translateYOutputRangeFor function', () => {
57
+ it('returns output range for position: top', () => {
58
+ expect(translateYOutputRangeFor({
59
+ position: 'top',
60
+ height: 20,
61
+ keyboardHeight: 0,
62
+ ...defaultOffsets
63
+ })).toEqual([-40, 40]);
64
+ });
65
+ it('returns output range for position: bottom', () => {
66
+ expect(translateYOutputRangeFor({
67
+ position: 'bottom',
68
+ height: 20,
69
+ keyboardHeight: 0,
70
+ ...defaultOffsets
71
+ })).toEqual([40, -60]);
72
+ });
73
+ it('returns output range for position: bottom, with keyboard offset', () => {
74
+ expect(translateYOutputRangeFor({
75
+ position: 'bottom',
76
+ height: 20,
77
+ keyboardHeight: 400,
78
+ ...defaultOffsets
79
+ })).toEqual([40, -405]);
80
+ });
81
+ it('throws if position is not supported', () => {
82
+ expect(() => translateYOutputRangeFor({
83
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
84
+ // @ts-ignore
85
+ position: 'left'
86
+ })).toThrow(new Error(`Position 'left' not supported`));
87
+ });
88
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,54 @@
1
+ /* eslint-env jest */
2
+ import { act, renderHook } from '~/__helpers__/testing-library/react-hooks';
3
+ import { useTimeout } from '../useTimeout';
4
+ const setup = () => {
5
+ const cb = jest.fn();
6
+ const utils = renderHook(() => useTimeout(cb));
7
+ return {
8
+ ...utils,
9
+ cb
10
+ };
11
+ };
12
+ describe('test useTimeout hook', () => {
13
+ beforeAll(() => {
14
+ jest.useFakeTimers();
15
+ });
16
+ it('sets timeout', () => {
17
+ const { cb, result } = setup();
18
+ act(() => {
19
+ result.current.startTimer();
20
+ });
21
+ expect(cb).not.toHaveBeenCalled();
22
+ act(() => {
23
+ jest.runAllTimers();
24
+ });
25
+ expect(cb).toHaveBeenCalled();
26
+ });
27
+ it('clears timeout before running', () => {
28
+ const { cb, result } = setup();
29
+ act(() => {
30
+ result.current.startTimer();
31
+ });
32
+ expect(cb).not.toHaveBeenCalled();
33
+ act(() => {
34
+ result.current.clearTimer();
35
+ });
36
+ expect(cb).not.toHaveBeenCalled();
37
+ act(() => {
38
+ jest.runAllTimers();
39
+ });
40
+ expect(cb).not.toHaveBeenCalled();
41
+ });
42
+ it('clears timeout when unmounting', () => {
43
+ const { cb, result, unmount } = setup();
44
+ act(() => {
45
+ result.current.startTimer();
46
+ });
47
+ expect(cb).not.toHaveBeenCalled();
48
+ act(() => {
49
+ unmount();
50
+ jest.runAllTimers();
51
+ });
52
+ expect(cb).not.toHaveBeenCalled();
53
+ });
54
+ });
@@ -0,0 +1,51 @@
1
+ /* eslint-env jest */
2
+ import { act } from 'react-test-renderer';
3
+ import { renderHook } from '~/__helpers__/testing-library/react-hooks';
4
+ import { useViewDimensions } from '../useViewDimensions';
5
+ const setup = (offsets) => {
6
+ const layoutChangeEventMock = {
7
+ nativeEvent: {
8
+ layout: {
9
+ height: 250,
10
+ width: 320
11
+ }
12
+ }
13
+ };
14
+ const utils = renderHook(() => useViewDimensions(offsets));
15
+ return {
16
+ ...utils,
17
+ layoutChangeEventMock
18
+ };
19
+ };
20
+ describe('test useViewDimensions hook', () => {
21
+ it('computes dimensions correctly', () => {
22
+ const { result, layoutChangeEventMock } = setup();
23
+ const { computeViewDimensions } = result.current;
24
+ act(() => {
25
+ computeViewDimensions(layoutChangeEventMock);
26
+ });
27
+ expect(result.current.height).toBe(250);
28
+ expect(result.current.width).toBe(320);
29
+ });
30
+ it('computes dimensions with offsets correctly', () => {
31
+ const offsets = {
32
+ heightOffset: -40,
33
+ widthOffset: 120
34
+ };
35
+ const { result, layoutChangeEventMock } = setup(offsets);
36
+ const { computeViewDimensions } = result.current;
37
+ act(() => {
38
+ computeViewDimensions(layoutChangeEventMock);
39
+ });
40
+ expect(result.current.height).toBe(250 + offsets.heightOffset);
41
+ expect(result.current.width).toBe(320 + offsets.widthOffset);
42
+ });
43
+ it('falls back to 0 when View dimensions are missing', () => {
44
+ const { result } = setup();
45
+ act(() => {
46
+ result.current.computeViewDimensions({});
47
+ });
48
+ expect(result.current.height).toBe(0);
49
+ expect(result.current.width).toBe(0);
50
+ });
51
+ });
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Animated } from 'react-native';
3
3
  import { ToastPosition } from '../types';
4
- declare type UseSlideAnimationParams = {
4
+ export declare type UseSlideAnimationParams = {
5
5
  position: ToastPosition;
6
6
  height: number;
7
7
  topOffset: number;
@@ -14,6 +14,11 @@ export declare function translateYOutputRangeFor({ position, height, topOffset,
14
14
  export declare function useSlideAnimation({ position, height, topOffset, bottomOffset, keyboardOffset }: UseSlideAnimationParams): {
15
15
  animatedValue: React.MutableRefObject<Animated.Value>;
16
16
  animate: (toValue: number) => void;
17
+ defaultStyles: {
18
+ transform: {
19
+ translateY: number;
20
+ }[];
21
+ };
17
22
  animationStyles: {
18
23
  opacity: Animated.AnimatedInterpolation;
19
24
  transform: {
@@ -21,4 +26,3 @@ export declare function useSlideAnimation({ position, height, topOffset, bottomO
21
26
  }[];
22
27
  };
23
28
  };
24
- export {};
@@ -1,26 +1,43 @@
1
1
  import React from 'react';
2
- import { Animated, Platform } from 'react-native';
3
- import { additiveInverseArray } from '../utils/array';
2
+ import { Animated } from 'react-native';
3
+ import { useEnvironmentContext } from '../contexts';
4
4
  import { useKeyboard } from './useKeyboard';
5
+ const startPointFor = (position, height) => {
6
+ const value = height * 2;
7
+ if (position === 'top') {
8
+ return -value;
9
+ }
10
+ return value;
11
+ };
5
12
  export function translateYOutputRangeFor({ position, height, topOffset, bottomOffset, keyboardHeight, keyboardOffset }) {
6
- const offset = position === 'bottom' ? bottomOffset : topOffset;
7
- const keyboardAwareOffset = position === 'bottom' ? keyboardHeight + keyboardOffset : 0;
8
- const range = [-(height * 2), Math.max(offset, keyboardAwareOffset)];
9
- const outputRange = position === 'bottom' ? additiveInverseArray(range) : range;
10
- return outputRange;
13
+ const startPoint = startPointFor(position, height);
14
+ switch (position) {
15
+ case 'bottom': {
16
+ const keyboardAwareOffset = keyboardHeight + keyboardOffset;
17
+ const range = [startPoint, -Math.max(bottomOffset, keyboardAwareOffset)];
18
+ return range;
19
+ }
20
+ case 'top': {
21
+ const keyboardAwareOffset = 0;
22
+ const range = [startPoint, Math.max(topOffset, keyboardAwareOffset)];
23
+ return range;
24
+ }
25
+ default:
26
+ throw new Error(`Position '${position}' not supported`);
27
+ }
11
28
  }
12
- const useNativeDriver = Platform.select({ native: true, default: false });
13
29
  export function useSlideAnimation({ position, height, topOffset, bottomOffset, keyboardOffset }) {
30
+ const { useNativeDriver } = useEnvironmentContext();
14
31
  const animatedValue = React.useRef(new Animated.Value(0));
15
32
  const { keyboardHeight } = useKeyboard();
16
33
  const animate = React.useCallback((toValue) => {
17
34
  Animated.spring(animatedValue.current, {
18
35
  toValue,
19
- useNativeDriver,
36
+ useNativeDriver: !!useNativeDriver,
20
37
  friction: 8
21
38
  }).start();
22
- }, []);
23
- const translateY = animatedValue.current.interpolate({
39
+ }, [useNativeDriver]);
40
+ const translateY = React.useMemo(() => animatedValue.current.interpolate({
24
41
  inputRange: [0, 1],
25
42
  outputRange: translateYOutputRangeFor({
26
43
  position,
@@ -30,14 +47,22 @@ export function useSlideAnimation({ position, height, topOffset, bottomOffset, k
30
47
  keyboardHeight,
31
48
  keyboardOffset
32
49
  })
33
- });
50
+ }), [position, height, topOffset, bottomOffset, keyboardHeight, keyboardOffset]);
34
51
  const opacity = animatedValue.current.interpolate({
35
52
  inputRange: [0, 0.7, 1],
36
53
  outputRange: [0, 1, 1]
37
54
  });
55
+ const defaultStyles = {
56
+ transform: [
57
+ {
58
+ translateY: startPointFor(position, height)
59
+ }
60
+ ]
61
+ };
38
62
  return {
39
63
  animatedValue,
40
64
  animate,
65
+ defaultStyles,
41
66
  animationStyles: {
42
67
  opacity,
43
68
  transform: [
@@ -154,6 +154,15 @@ export declare type ToastProps = {
154
154
  * Default value: `10`
155
155
  */
156
156
  keyboardOffset?: number;
157
+ /**
158
+ * Enable / disable native driver for the slide animation.
159
+ *
160
+ * Default:
161
+ * - `true` on iOS
162
+ * - `false` on Android (fixes [issue #300](https://github.com/calintamas/react-native-toast-message/issues/300))
163
+ * - `false` on Web (fixes [issue #311](https://github.com/calintamas/react-native-toast-message/issues/311))
164
+ */
165
+ useNativeDriver?: boolean;
157
166
  /**
158
167
  * Called when any Toast is shown
159
168
  */
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ /* eslint-env jest */
2
+ import { bound, lowerBound, upperBound } from '../number';
3
+ describe('test bound functions', () => {
4
+ it('ensures lower bound', () => {
5
+ expect(lowerBound(10, 2)).toBe(10);
6
+ expect(lowerBound(0, 2)).toBe(2);
7
+ });
8
+ it('ensures upper bound', () => {
9
+ expect(upperBound(10, 2)).toBe(2);
10
+ expect(upperBound(0, 2)).toBe(0);
11
+ });
12
+ it('ensures both bounds', () => {
13
+ expect(bound(10, 2, 5)).toBe(5);
14
+ });
15
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ /* eslint-env jest */
2
+ import { mergeIfDefined } from '../obj';
3
+ describe('test mergeIfDefined function', () => {
4
+ it('merges defined values from obj2', () => {
5
+ const obj1 = {
6
+ foo: 1,
7
+ bar: 2,
8
+ baz: 3
9
+ };
10
+ const obj2 = {
11
+ foo: null,
12
+ bar: 20,
13
+ baz: undefined
14
+ };
15
+ expect(mergeIfDefined(obj1, obj2)).toEqual({
16
+ foo: 1,
17
+ bar: 20,
18
+ baz: 3
19
+ });
20
+ });
21
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-toast-message",
3
- "version": "2.1.3",
3
+ "version": "2.2.0-beta.1",
4
4
  "description": "Toast message component for React Native",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -22,27 +22,32 @@
22
22
  "lint": "./node_modules/.bin/eslint --fix",
23
23
  "lint-staged": "./node_modules/.bin/lint-staged",
24
24
  "test": "./node_modules/.bin/jest",
25
- "yalc:push": "yarn build && yalc publish --push",
26
- "quality": "yarn lint && tsc --noEmit"
25
+ "quality": "yarn lint && tsc --noEmit",
26
+ "yalc:watch": "rm -rf ./lib && tsc-watch --onCompilationComplete \"yalc publish --push\""
27
27
  },
28
28
  "author": "Calin Tamas <calintamas2@gmail.com>",
29
29
  "license": "MIT",
30
30
  "devDependencies": {
31
31
  "@babel/core": "^7.15.8",
32
32
  "@testing-library/jest-native": "^4.0.4",
33
- "@testing-library/react-hooks": "^7.0.2",
33
+ "@testing-library/react-hooks": "^8.0.0",
34
34
  "@testing-library/react-native": "^9.0.0",
35
35
  "@types/jest": "^27.0.1",
36
- "@types/react-native": "^0.67.3",
36
+ "@types/react-native": "^0.67.7",
37
+ "@types/react-test-renderer": "^18.0.0",
38
+ "babel-plugin-module-resolver": "^4.1.0",
37
39
  "eslint-config-backpacker-react-ts": "^0.3.0",
38
- "husky": "^7.0.2",
39
- "import-sort-style-module": "^6.0.0",
40
+ "husky": "^8.0.1",
41
+ "import-sort-style-module-alias": "^1.1.0",
40
42
  "jest": "^27.1.1",
41
- "lint-staged": "^12.1.2",
42
- "metro-react-native-babel-preset": "^0.69.0",
43
+ "lint-staged": "^13.0.0",
44
+ "metro-react-native-babel-preset": "^0.71.0",
43
45
  "prettier": "^2.4.1",
44
46
  "prettier-plugin-import-sort": "^0.0.7",
47
+ "react": "17.0.2",
48
+ "react-native": "0.68.2",
45
49
  "react-test-renderer": "^17.0.2",
50
+ "tsc-watch": "^5.0.3",
46
51
  "typescript": "^4.4.3",
47
52
  "yalc": "^1.0.0-pre.53"
48
53
  },
@@ -52,8 +57,13 @@
52
57
  },
53
58
  "importSort": {
54
59
  ".js, .jsx, .ts, .tsx": {
55
- "style": "module",
56
- "parser": "typescript"
60
+ "style": "module-alias",
61
+ "parser": "typescript",
62
+ "options": {
63
+ "alias": [
64
+ "~"
65
+ ]
66
+ }
57
67
  }
58
68
  },
59
69
  "lint-staged": {