@pagopa/io-app-design-system 5.11.11 → 5.11.12
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/commonjs/components/templates/ForceScrollDownView.js +43 -73
- package/lib/commonjs/components/templates/ForceScrollDownView.js.map +1 -1
- package/lib/module/components/templates/ForceScrollDownView.js +45 -75
- package/lib/module/components/templates/ForceScrollDownView.js.map +1 -1
- package/lib/typescript/components/templates/ForceScrollDownView.d.ts +7 -1
- package/lib/typescript/components/templates/ForceScrollDownView.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/templates/ForceScrollDownView.tsx +72 -104
- package/lib/commonjs/components/templates/__test__/ForceScrollDownView.test.js +0 -115
- package/lib/commonjs/components/templates/__test__/ForceScrollDownView.test.js.map +0 -1
- package/lib/commonjs/components/templates/__test__/__snapshots__/ForceScrollDownView.test.tsx.snap +0 -18
- package/lib/module/components/templates/__test__/ForceScrollDownView.test.js +0 -113
- package/lib/module/components/templates/__test__/ForceScrollDownView.test.js.map +0 -1
- package/lib/module/components/templates/__test__/__snapshots__/ForceScrollDownView.test.tsx.snap +0 -18
- package/lib/typescript/components/templates/__test__/ForceScrollDownView.test.d.ts +0 -2
- package/lib/typescript/components/templates/__test__/ForceScrollDownView.test.d.ts.map +0 -1
- package/src/components/templates/__test__/ForceScrollDownView.test.tsx +0 -106
- package/src/components/templates/__test__/__snapshots__/ForceScrollDownView.test.tsx.snap +0 -18
|
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.ForceScrollDownView = void 0;
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
|
+
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
|
|
9
10
|
var _core = require("../../core");
|
|
10
11
|
var _buttons = require("../buttons");
|
|
11
|
-
var _ScaleInOutAnimation = require("../common/ScaleInOutAnimation");
|
|
12
12
|
var _layout = require("../layout");
|
|
13
13
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
14
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
@@ -25,109 +25,81 @@ const ForceScrollDownView = ({
|
|
|
25
25
|
style,
|
|
26
26
|
contentContainerStyle,
|
|
27
27
|
scrollEnabled = true,
|
|
28
|
-
onThresholdCrossed
|
|
28
|
+
onThresholdCrossed,
|
|
29
|
+
animatedRef
|
|
29
30
|
}) => {
|
|
30
|
-
const
|
|
31
|
+
const internalAnimatedRef = (0, _reactNativeReanimated.useAnimatedRef)();
|
|
32
|
+
const scrollViewRef = animatedRef ?? internalAnimatedRef;
|
|
31
33
|
const {
|
|
32
34
|
footerActionsInlineMeasurements,
|
|
33
35
|
handleFooterActionsInlineMeasurements
|
|
34
36
|
} = (0, _layout.useFooterActionsInlineMeasurements)();
|
|
35
37
|
const threshold = footerActions ? footerActionsInlineMeasurements.safeBottomAreaHeight : customThreshold;
|
|
36
38
|
|
|
37
|
-
/**
|
|
38
|
-
* The height of the scroll view, used to determine whether or not the scrollable content fits inside
|
|
39
|
-
* the scroll view and whether the "scroll to bottom" button should be displayed.
|
|
40
|
-
*/
|
|
41
|
-
const [scrollViewHeight, setScrollViewHeight] = (0, _react.useState)(0);
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* The height of the scrollable content, used to determine whether or not the "scroll to bottom" button
|
|
45
|
-
* should be displayed.
|
|
46
|
-
*/
|
|
47
|
-
const [contentHeight, setContentHeight] = (0, _react.useState)(0);
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Whether or not the scroll view has crossed the threshold from the bottom.
|
|
51
|
-
*/
|
|
52
|
-
const [isThresholdCrossed, setThresholdCrossed] = (0, _react.useState)(false);
|
|
53
|
-
|
|
54
39
|
/**
|
|
55
40
|
* Whether or not the "scroll to bottom" button should be visible. This is controlled by the threshold
|
|
56
41
|
* and the current scroll position.
|
|
57
42
|
*/
|
|
58
|
-
const [isButtonVisible, setButtonVisible] =
|
|
43
|
+
// const [isButtonVisible, setButtonVisible] = useState(true);
|
|
59
44
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const thresholdCrossed = layoutMeasurement.height + contentOffset.y >= contentSize.height - (threshold ?? 0);
|
|
72
|
-
if (isThresholdCrossed !== thresholdCrossed) {
|
|
73
|
-
setThresholdCrossed(thresholdCrossed);
|
|
74
|
-
setButtonVisible(!thresholdCrossed);
|
|
45
|
+
const isButtonVisible = (0, _reactNativeReanimated.useSharedValue)(1);
|
|
46
|
+
const scrollViewHeight = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
47
|
+
const contentHeight = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
48
|
+
const offsetY = (0, _reactNativeReanimated.useScrollViewOffset)(scrollViewRef);
|
|
49
|
+
(0, _reactNativeReanimated.useAnimatedReaction)(() => scrollViewHeight.value + Math.max(offsetY.value, 0) >= contentHeight.value - (threshold ?? 0), (crossed, previous) => {
|
|
50
|
+
if (crossed !== previous) {
|
|
51
|
+
// eslint-disable-next-line functional/immutable-data
|
|
52
|
+
isButtonVisible.value = (0, _reactNativeReanimated.withSpring)(crossed && scrollEnabled ? 0 : 1, _core.IOSpringValues.button);
|
|
53
|
+
if (onThresholdCrossed) {
|
|
54
|
+
(0, _reactNativeReanimated.runOnJS)(onThresholdCrossed)(crossed);
|
|
55
|
+
}
|
|
75
56
|
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* A side effect that calls the `onThresholdCrossed` callback whenever the value of `isThresholdCrossed` changes.
|
|
80
|
-
*/
|
|
81
|
-
(0, _react.useEffect)(() => {
|
|
82
|
-
onThresholdCrossed?.(isThresholdCrossed);
|
|
83
|
-
}, [onThresholdCrossed, isThresholdCrossed]);
|
|
57
|
+
});
|
|
84
58
|
|
|
85
59
|
/**
|
|
86
60
|
* A callback that is called whenever the size of the scrollable content changes. It updates the
|
|
87
61
|
* state with the new content height.
|
|
88
62
|
*/
|
|
89
|
-
const handleContentSizeChange = (0, _react.useCallback)(
|
|
90
|
-
|
|
91
|
-
|
|
63
|
+
const handleContentSizeChange = (0, _react.useCallback)(
|
|
64
|
+
// eslint-disable-next-line functional/immutable-data
|
|
65
|
+
(_w, h) => contentHeight.value = h, [contentHeight]);
|
|
92
66
|
|
|
93
67
|
/**
|
|
94
68
|
* A callback that is called whenever the size of the scroll view changes. It updates the state
|
|
95
69
|
* with the new scroll view height.
|
|
96
70
|
*/
|
|
97
|
-
const handleLayout = (0, _react.useCallback)(event =>
|
|
98
|
-
|
|
99
|
-
|
|
71
|
+
const handleLayout = (0, _react.useCallback)(event =>
|
|
72
|
+
// eslint-disable-next-line functional/immutable-data
|
|
73
|
+
scrollViewHeight.value = event.nativeEvent.layout.height, [scrollViewHeight]);
|
|
100
74
|
|
|
101
75
|
/**
|
|
102
76
|
* A callback that is called when the "scroll to bottom" button is pressed. It scrolls the
|
|
103
77
|
* scroll view to the bottom and hides the button.
|
|
104
78
|
*/
|
|
105
79
|
const handleScrollDownPress = (0, _react.useCallback)(() => {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}, [scrollViewRef]);
|
|
80
|
+
(0, _reactNativeReanimated.runOnUI)(() => {
|
|
81
|
+
"worklet";
|
|
109
82
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
83
|
+
// eslint-disable-next-line functional/immutable-data
|
|
84
|
+
isButtonVisible.value = (0, _reactNativeReanimated.withSpring)(0, _core.IOSpringValues.button);
|
|
85
|
+
const targetY = Math.max(0, contentHeight.value - scrollViewHeight.value);
|
|
86
|
+
(0, _reactNativeReanimated.scrollTo)(scrollViewRef, 0, targetY, true);
|
|
87
|
+
})();
|
|
88
|
+
}, [scrollViewRef, contentHeight, scrollViewHeight, isButtonVisible]);
|
|
116
89
|
|
|
117
90
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
91
|
+
* The "scroll to bottom" button component. It is wrapped in a reanimated View
|
|
92
|
+
* and has animated style applied to it.
|
|
120
93
|
*/
|
|
121
|
-
const shouldRenderScrollButton = scrollEnabled && needsScroll && isButtonVisible;
|
|
122
94
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
95
|
+
const buttonTransitionStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => ({
|
|
96
|
+
opacity: isButtonVisible.value,
|
|
97
|
+
transform: [{
|
|
98
|
+
scale: (0, _reactNativeReanimated.interpolate)(isButtonVisible.value, [0, 1], [0.5, 1])
|
|
99
|
+
}]
|
|
100
|
+
}));
|
|
101
|
+
const scrollDownButton = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
|
|
102
|
+
style: [styles.scrollDownButton, buttonTransitionStyle],
|
|
131
103
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_buttons.IconButtonSolid, {
|
|
132
104
|
testID: "ScrollDownButton",
|
|
133
105
|
accessibilityLabel: "Scroll to bottom",
|
|
@@ -136,13 +108,11 @@ const ForceScrollDownView = ({
|
|
|
136
108
|
})
|
|
137
109
|
});
|
|
138
110
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
139
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(
|
|
111
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.ScrollView, {
|
|
140
112
|
testID: "ScrollView",
|
|
141
113
|
ref: scrollViewRef,
|
|
142
114
|
scrollEnabled: scrollEnabled,
|
|
143
115
|
style: style,
|
|
144
|
-
onScroll: handleScroll,
|
|
145
|
-
scrollEventThrottle: 8,
|
|
146
116
|
onLayout: handleLayout,
|
|
147
117
|
onContentSizeChange: handleContentSizeChange,
|
|
148
118
|
contentContainerStyle: contentContainerStyle,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","_core","_buttons","_layout","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","ForceScrollDownView","footerActions","children","threshold","customThreshold","style","contentContainerStyle","scrollEnabled","onThresholdCrossed","animatedRef","internalAnimatedRef","useAnimatedRef","scrollViewRef","footerActionsInlineMeasurements","handleFooterActionsInlineMeasurements","useFooterActionsInlineMeasurements","safeBottomAreaHeight","isButtonVisible","useSharedValue","scrollViewHeight","contentHeight","offsetY","useScrollViewOffset","useAnimatedReaction","value","Math","max","crossed","previous","withSpring","IOSpringValues","button","runOnJS","handleContentSizeChange","useCallback","_w","h","handleLayout","event","nativeEvent","layout","height","handleScrollDownPress","runOnUI","targetY","scrollTo","buttonTransitionStyle","useAnimatedStyle","opacity","transform","scale","interpolate","scrollDownButton","jsx","View","styles","IconButtonSolid","testID","accessibilityLabel","icon","onPress","jsxs","Fragment","ScrollView","ref","onLayout","onContentSizeChange","FooterActions","onMeasure","fixed","exports","StyleSheet","create","position","zIndex","right","IOVisualCostants","scrollDownButtonRight","bottom","scrollDownButtonBottom"],"sourceRoot":"../../../../src","sources":["components/templates/ForceScrollDownView.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { useCallback
|
|
4
|
-
import {
|
|
3
|
+
import React, { useCallback } from "react";
|
|
4
|
+
import { StyleSheet } from "react-native";
|
|
5
|
+
import Animated, { interpolate, runOnJS, runOnUI, scrollTo, useAnimatedReaction, useAnimatedRef, useAnimatedStyle, useScrollViewOffset, useSharedValue, withSpring } from "react-native-reanimated";
|
|
5
6
|
import { IOSpringValues, IOVisualCostants } from "../../core";
|
|
6
7
|
import { IconButtonSolid } from "../buttons";
|
|
7
|
-
import { ScaleInOutAnimation } from "../common/ScaleInOutAnimation";
|
|
8
8
|
import { FooterActions, useFooterActionsInlineMeasurements } from "../layout";
|
|
9
9
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
10
10
|
/**
|
|
@@ -20,109 +20,81 @@ const ForceScrollDownView = ({
|
|
|
20
20
|
style,
|
|
21
21
|
contentContainerStyle,
|
|
22
22
|
scrollEnabled = true,
|
|
23
|
-
onThresholdCrossed
|
|
23
|
+
onThresholdCrossed,
|
|
24
|
+
animatedRef
|
|
24
25
|
}) => {
|
|
25
|
-
const
|
|
26
|
+
const internalAnimatedRef = useAnimatedRef();
|
|
27
|
+
const scrollViewRef = animatedRef ?? internalAnimatedRef;
|
|
26
28
|
const {
|
|
27
29
|
footerActionsInlineMeasurements,
|
|
28
30
|
handleFooterActionsInlineMeasurements
|
|
29
31
|
} = useFooterActionsInlineMeasurements();
|
|
30
32
|
const threshold = footerActions ? footerActionsInlineMeasurements.safeBottomAreaHeight : customThreshold;
|
|
31
33
|
|
|
32
|
-
/**
|
|
33
|
-
* The height of the scroll view, used to determine whether or not the scrollable content fits inside
|
|
34
|
-
* the scroll view and whether the "scroll to bottom" button should be displayed.
|
|
35
|
-
*/
|
|
36
|
-
const [scrollViewHeight, setScrollViewHeight] = useState(0);
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* The height of the scrollable content, used to determine whether or not the "scroll to bottom" button
|
|
40
|
-
* should be displayed.
|
|
41
|
-
*/
|
|
42
|
-
const [contentHeight, setContentHeight] = useState(0);
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Whether or not the scroll view has crossed the threshold from the bottom.
|
|
46
|
-
*/
|
|
47
|
-
const [isThresholdCrossed, setThresholdCrossed] = useState(false);
|
|
48
|
-
|
|
49
34
|
/**
|
|
50
35
|
* Whether or not the "scroll to bottom" button should be visible. This is controlled by the threshold
|
|
51
36
|
* and the current scroll position.
|
|
52
37
|
*/
|
|
53
|
-
const [isButtonVisible, setButtonVisible] = useState(true);
|
|
38
|
+
// const [isButtonVisible, setButtonVisible] = useState(true);
|
|
54
39
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const thresholdCrossed = layoutMeasurement.height + contentOffset.y >= contentSize.height - (threshold ?? 0);
|
|
67
|
-
if (isThresholdCrossed !== thresholdCrossed) {
|
|
68
|
-
setThresholdCrossed(thresholdCrossed);
|
|
69
|
-
setButtonVisible(!thresholdCrossed);
|
|
40
|
+
const isButtonVisible = useSharedValue(1);
|
|
41
|
+
const scrollViewHeight = useSharedValue(0);
|
|
42
|
+
const contentHeight = useSharedValue(0);
|
|
43
|
+
const offsetY = useScrollViewOffset(scrollViewRef);
|
|
44
|
+
useAnimatedReaction(() => scrollViewHeight.value + Math.max(offsetY.value, 0) >= contentHeight.value - (threshold ?? 0), (crossed, previous) => {
|
|
45
|
+
if (crossed !== previous) {
|
|
46
|
+
// eslint-disable-next-line functional/immutable-data
|
|
47
|
+
isButtonVisible.value = withSpring(crossed && scrollEnabled ? 0 : 1, IOSpringValues.button);
|
|
48
|
+
if (onThresholdCrossed) {
|
|
49
|
+
runOnJS(onThresholdCrossed)(crossed);
|
|
50
|
+
}
|
|
70
51
|
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* A side effect that calls the `onThresholdCrossed` callback whenever the value of `isThresholdCrossed` changes.
|
|
75
|
-
*/
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
onThresholdCrossed?.(isThresholdCrossed);
|
|
78
|
-
}, [onThresholdCrossed, isThresholdCrossed]);
|
|
52
|
+
});
|
|
79
53
|
|
|
80
54
|
/**
|
|
81
55
|
* A callback that is called whenever the size of the scrollable content changes. It updates the
|
|
82
56
|
* state with the new content height.
|
|
83
57
|
*/
|
|
84
|
-
const handleContentSizeChange = useCallback(
|
|
85
|
-
|
|
86
|
-
|
|
58
|
+
const handleContentSizeChange = useCallback(
|
|
59
|
+
// eslint-disable-next-line functional/immutable-data
|
|
60
|
+
(_w, h) => contentHeight.value = h, [contentHeight]);
|
|
87
61
|
|
|
88
62
|
/**
|
|
89
63
|
* A callback that is called whenever the size of the scroll view changes. It updates the state
|
|
90
64
|
* with the new scroll view height.
|
|
91
65
|
*/
|
|
92
|
-
const handleLayout = useCallback(event =>
|
|
93
|
-
|
|
94
|
-
|
|
66
|
+
const handleLayout = useCallback(event =>
|
|
67
|
+
// eslint-disable-next-line functional/immutable-data
|
|
68
|
+
scrollViewHeight.value = event.nativeEvent.layout.height, [scrollViewHeight]);
|
|
95
69
|
|
|
96
70
|
/**
|
|
97
71
|
* A callback that is called when the "scroll to bottom" button is pressed. It scrolls the
|
|
98
72
|
* scroll view to the bottom and hides the button.
|
|
99
73
|
*/
|
|
100
74
|
const handleScrollDownPress = useCallback(() => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}, [scrollViewRef]);
|
|
75
|
+
runOnUI(() => {
|
|
76
|
+
"worklet";
|
|
104
77
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
78
|
+
// eslint-disable-next-line functional/immutable-data
|
|
79
|
+
isButtonVisible.value = withSpring(0, IOSpringValues.button);
|
|
80
|
+
const targetY = Math.max(0, contentHeight.value - scrollViewHeight.value);
|
|
81
|
+
scrollTo(scrollViewRef, 0, targetY, true);
|
|
82
|
+
})();
|
|
83
|
+
}, [scrollViewRef, contentHeight, scrollViewHeight, isButtonVisible]);
|
|
111
84
|
|
|
112
85
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
86
|
+
* The "scroll to bottom" button component. It is wrapped in a reanimated View
|
|
87
|
+
* and has animated style applied to it.
|
|
115
88
|
*/
|
|
116
|
-
const shouldRenderScrollButton = scrollEnabled && needsScroll && isButtonVisible;
|
|
117
89
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
90
|
+
const buttonTransitionStyle = useAnimatedStyle(() => ({
|
|
91
|
+
opacity: isButtonVisible.value,
|
|
92
|
+
transform: [{
|
|
93
|
+
scale: interpolate(isButtonVisible.value, [0, 1], [0.5, 1])
|
|
94
|
+
}]
|
|
95
|
+
}));
|
|
96
|
+
const scrollDownButton = /*#__PURE__*/_jsx(Animated.View, {
|
|
97
|
+
style: [styles.scrollDownButton, buttonTransitionStyle],
|
|
126
98
|
children: /*#__PURE__*/_jsx(IconButtonSolid, {
|
|
127
99
|
testID: "ScrollDownButton",
|
|
128
100
|
accessibilityLabel: "Scroll to bottom",
|
|
@@ -131,13 +103,11 @@ const ForceScrollDownView = ({
|
|
|
131
103
|
})
|
|
132
104
|
});
|
|
133
105
|
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
134
|
-
children: [/*#__PURE__*/_jsxs(ScrollView, {
|
|
106
|
+
children: [/*#__PURE__*/_jsxs(Animated.ScrollView, {
|
|
135
107
|
testID: "ScrollView",
|
|
136
108
|
ref: scrollViewRef,
|
|
137
109
|
scrollEnabled: scrollEnabled,
|
|
138
110
|
style: style,
|
|
139
|
-
onScroll: handleScroll,
|
|
140
|
-
scrollEventThrottle: 8,
|
|
141
111
|
onLayout: handleLayout,
|
|
142
112
|
onContentSizeChange: handleContentSizeChange,
|
|
143
113
|
contentContainerStyle: contentContainerStyle,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","useCallback","
|
|
1
|
+
{"version":3,"names":["React","useCallback","StyleSheet","Animated","interpolate","runOnJS","runOnUI","scrollTo","useAnimatedReaction","useAnimatedRef","useAnimatedStyle","useScrollViewOffset","useSharedValue","withSpring","IOSpringValues","IOVisualCostants","IconButtonSolid","FooterActions","useFooterActionsInlineMeasurements","jsx","_jsx","jsxs","_jsxs","Fragment","_Fragment","ForceScrollDownView","footerActions","children","threshold","customThreshold","style","contentContainerStyle","scrollEnabled","onThresholdCrossed","animatedRef","internalAnimatedRef","scrollViewRef","footerActionsInlineMeasurements","handleFooterActionsInlineMeasurements","safeBottomAreaHeight","isButtonVisible","scrollViewHeight","contentHeight","offsetY","value","Math","max","crossed","previous","button","handleContentSizeChange","_w","h","handleLayout","event","nativeEvent","layout","height","handleScrollDownPress","targetY","buttonTransitionStyle","opacity","transform","scale","scrollDownButton","View","styles","testID","accessibilityLabel","icon","onPress","ScrollView","ref","onLayout","onContentSizeChange","onMeasure","fixed","create","position","zIndex","right","scrollDownButtonRight","bottom","scrollDownButtonBottom"],"sourceRoot":"../../../../src","sources":["components/templates/ForceScrollDownView.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { ComponentProps, ReactNode } from "react";
|
|
2
2
|
import { ScrollViewProps } from "react-native";
|
|
3
|
+
import Animated, { AnimatedRef } from "react-native-reanimated";
|
|
3
4
|
import { FooterActions } from "../layout";
|
|
4
5
|
type ForceScrollDownViewActions = {
|
|
5
6
|
/**
|
|
@@ -27,6 +28,11 @@ export type ForceScrollDownView = {
|
|
|
27
28
|
* is passed a boolean indicating whether the threshold has been crossed (`true`) or not (`false`).
|
|
28
29
|
*/
|
|
29
30
|
onThresholdCrossed?: (crossed: boolean) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Optional Animated ref to be used with `useScrollViewOffset`
|
|
33
|
+
* (outside this component)
|
|
34
|
+
*/
|
|
35
|
+
animatedRef?: AnimatedRef<Animated.ScrollView>;
|
|
30
36
|
} & ForceScrollDownViewSlot & Pick<ScrollViewProps, "style" | "contentContainerStyle" | "scrollEnabled" | "testID">;
|
|
31
37
|
/**
|
|
32
38
|
* A React Native component that displays a scroll view with a button that scrolls to the bottom of the content
|
|
@@ -34,6 +40,6 @@ export type ForceScrollDownView = {
|
|
|
34
40
|
* configurable by the `threshold` prop. The button, and the scrolling, can also be disabled by setting the
|
|
35
41
|
* `scrollEnabled` prop to `false`.
|
|
36
42
|
*/
|
|
37
|
-
declare const ForceScrollDownView: ({ footerActions, children, threshold: customThreshold, style, contentContainerStyle, scrollEnabled, onThresholdCrossed }: ForceScrollDownView) => React.JSX.Element;
|
|
43
|
+
declare const ForceScrollDownView: ({ footerActions, children, threshold: customThreshold, style, contentContainerStyle, scrollEnabled, onThresholdCrossed, animatedRef }: ForceScrollDownView) => React.JSX.Element;
|
|
38
44
|
export { ForceScrollDownView };
|
|
39
45
|
//# sourceMappingURL=ForceScrollDownView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ForceScrollDownView.d.ts","sourceRoot":"","sources":["../../../../src/components/templates/ForceScrollDownView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"ForceScrollDownView.d.ts","sourceRoot":"","sources":["../../../../src/components/templates/ForceScrollDownView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,cAAc,EAAE,SAAS,EAAe,MAAM,OAAO,CAAC;AACtE,OAAO,EAAqB,eAAe,EAAc,MAAM,cAAc,CAAC;AAC9E,OAAO,QAAQ,EAAE,EACf,WAAW,EAWZ,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,aAAa,EAAsC,MAAM,WAAW,CAAC;AAE9E,KAAK,0BAA0B,GAAG;IAChC;;OAEG;IACH,SAAS,CAAC,EAAE,KAAK,CAAC;IAClB,aAAa,EAAE,IAAI,CACjB,cAAc,CAAC,OAAO,aAAa,CAAC,EACpC,OAAO,GAAG,WAAW,CACtB,CAAC;CACH,CAAC;AAEF,KAAK,6BAA6B,GAAG;IACnC;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,KAAK,CAAC;CACvB,CAAC;AAEF,KAAK,uBAAuB,GACxB,0BAA0B,GAC1B,6BAA6B,CAAC;AAElC,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;CAChD,GAAG,uBAAuB,GACzB,IAAI,CACF,eAAe,EACf,OAAO,GAAG,uBAAuB,GAAG,eAAe,GAAG,QAAQ,CAC/D,CAAC;AAEJ;;;;;GAKG;AACH,QAAA,MAAM,mBAAmB,GAAI,uIAS1B,mBAAmB,sBAyHrB,CAAC;AAWF,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
StyleSheet
|
|
17
|
-
} from "react-native";
|
|
1
|
+
import React, { ComponentProps, ReactNode, useCallback } from "react";
|
|
2
|
+
import { LayoutChangeEvent, ScrollViewProps, StyleSheet } from "react-native";
|
|
3
|
+
import Animated, {
|
|
4
|
+
AnimatedRef,
|
|
5
|
+
interpolate,
|
|
6
|
+
runOnJS,
|
|
7
|
+
runOnUI,
|
|
8
|
+
scrollTo,
|
|
9
|
+
useAnimatedReaction,
|
|
10
|
+
useAnimatedRef,
|
|
11
|
+
useAnimatedStyle,
|
|
12
|
+
useScrollViewOffset,
|
|
13
|
+
useSharedValue,
|
|
14
|
+
withSpring
|
|
15
|
+
} from "react-native-reanimated";
|
|
18
16
|
import { IOSpringValues, IOVisualCostants } from "../../core";
|
|
19
17
|
import { IconButtonSolid } from "../buttons";
|
|
20
|
-
import { ScaleInOutAnimation } from "../common/ScaleInOutAnimation";
|
|
21
18
|
import { FooterActions, useFooterActionsInlineMeasurements } from "../layout";
|
|
22
19
|
|
|
23
20
|
type ForceScrollDownViewActions = {
|
|
@@ -54,6 +51,11 @@ export type ForceScrollDownView = {
|
|
|
54
51
|
* is passed a boolean indicating whether the threshold has been crossed (`true`) or not (`false`).
|
|
55
52
|
*/
|
|
56
53
|
onThresholdCrossed?: (crossed: boolean) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Optional Animated ref to be used with `useScrollViewOffset`
|
|
56
|
+
* (outside this component)
|
|
57
|
+
*/
|
|
58
|
+
animatedRef?: AnimatedRef<Animated.ScrollView>;
|
|
57
59
|
} & ForceScrollDownViewSlot &
|
|
58
60
|
Pick<
|
|
59
61
|
ScrollViewProps,
|
|
@@ -73,9 +75,11 @@ const ForceScrollDownView = ({
|
|
|
73
75
|
style,
|
|
74
76
|
contentContainerStyle,
|
|
75
77
|
scrollEnabled = true,
|
|
76
|
-
onThresholdCrossed
|
|
78
|
+
onThresholdCrossed,
|
|
79
|
+
animatedRef
|
|
77
80
|
}: ForceScrollDownView) => {
|
|
78
|
-
const
|
|
81
|
+
const internalAnimatedRef = useAnimatedRef<Animated.ScrollView>();
|
|
82
|
+
const scrollViewRef = animatedRef ?? internalAnimatedRef;
|
|
79
83
|
|
|
80
84
|
const {
|
|
81
85
|
footerActionsInlineMeasurements,
|
|
@@ -86,134 +90,98 @@ const ForceScrollDownView = ({
|
|
|
86
90
|
? footerActionsInlineMeasurements.safeBottomAreaHeight
|
|
87
91
|
: customThreshold;
|
|
88
92
|
|
|
89
|
-
/**
|
|
90
|
-
* The height of the scroll view, used to determine whether or not the scrollable content fits inside
|
|
91
|
-
* the scroll view and whether the "scroll to bottom" button should be displayed.
|
|
92
|
-
*/
|
|
93
|
-
const [scrollViewHeight, setScrollViewHeight] = useState<number>(0);
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* The height of the scrollable content, used to determine whether or not the "scroll to bottom" button
|
|
97
|
-
* should be displayed.
|
|
98
|
-
*/
|
|
99
|
-
const [contentHeight, setContentHeight] = useState<number>(0);
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Whether or not the scroll view has crossed the threshold from the bottom.
|
|
103
|
-
*/
|
|
104
|
-
const [isThresholdCrossed, setThresholdCrossed] = useState(false);
|
|
105
|
-
|
|
106
93
|
/**
|
|
107
94
|
* Whether or not the "scroll to bottom" button should be visible. This is controlled by the threshold
|
|
108
95
|
* and the current scroll position.
|
|
109
96
|
*/
|
|
110
|
-
const [isButtonVisible, setButtonVisible] = useState(true);
|
|
97
|
+
// const [isButtonVisible, setButtonVisible] = useState(true);
|
|
111
98
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
99
|
+
const isButtonVisible = useSharedValue(1);
|
|
100
|
+
const scrollViewHeight = useSharedValue(0);
|
|
101
|
+
const contentHeight = useSharedValue(0);
|
|
102
|
+
const offsetY = useScrollViewOffset(scrollViewRef);
|
|
103
|
+
|
|
104
|
+
useAnimatedReaction(
|
|
105
|
+
() =>
|
|
106
|
+
scrollViewHeight.value + Math.max(offsetY.value, 0) >=
|
|
107
|
+
contentHeight.value - (threshold ?? 0),
|
|
108
|
+
(crossed, previous) => {
|
|
109
|
+
if (crossed !== previous) {
|
|
110
|
+
// eslint-disable-next-line functional/immutable-data
|
|
111
|
+
isButtonVisible.value = withSpring(
|
|
112
|
+
crossed && scrollEnabled ? 0 : 1,
|
|
113
|
+
IOSpringValues.button
|
|
114
|
+
);
|
|
115
|
+
if (onThresholdCrossed) {
|
|
116
|
+
runOnJS(onThresholdCrossed)(crossed);
|
|
117
|
+
}
|
|
129
118
|
}
|
|
130
|
-
}
|
|
131
|
-
[threshold, isThresholdCrossed]
|
|
119
|
+
}
|
|
132
120
|
);
|
|
133
121
|
|
|
134
|
-
/**
|
|
135
|
-
* A side effect that calls the `onThresholdCrossed` callback whenever the value of `isThresholdCrossed` changes.
|
|
136
|
-
*/
|
|
137
|
-
useEffect(() => {
|
|
138
|
-
onThresholdCrossed?.(isThresholdCrossed);
|
|
139
|
-
}, [onThresholdCrossed, isThresholdCrossed]);
|
|
140
|
-
|
|
141
122
|
/**
|
|
142
123
|
* A callback that is called whenever the size of the scrollable content changes. It updates the
|
|
143
124
|
* state with the new content height.
|
|
144
125
|
*/
|
|
145
126
|
const handleContentSizeChange = useCallback(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
[]
|
|
127
|
+
// eslint-disable-next-line functional/immutable-data
|
|
128
|
+
(_w: number, h: number) => (contentHeight.value = h),
|
|
129
|
+
[contentHeight]
|
|
150
130
|
);
|
|
151
131
|
|
|
152
132
|
/**
|
|
153
133
|
* A callback that is called whenever the size of the scroll view changes. It updates the state
|
|
154
134
|
* with the new scroll view height.
|
|
155
135
|
*/
|
|
156
|
-
const handleLayout = useCallback(
|
|
157
|
-
|
|
158
|
-
|
|
136
|
+
const handleLayout = useCallback(
|
|
137
|
+
(event: LayoutChangeEvent) =>
|
|
138
|
+
// eslint-disable-next-line functional/immutable-data
|
|
139
|
+
(scrollViewHeight.value = event.nativeEvent.layout.height),
|
|
140
|
+
[scrollViewHeight]
|
|
141
|
+
);
|
|
159
142
|
|
|
160
143
|
/**
|
|
161
144
|
* A callback that is called when the "scroll to bottom" button is pressed. It scrolls the
|
|
162
145
|
* scroll view to the bottom and hides the button.
|
|
163
146
|
*/
|
|
164
147
|
const handleScrollDownPress = useCallback(() => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
148
|
+
runOnUI(() => {
|
|
149
|
+
"worklet";
|
|
150
|
+
// eslint-disable-next-line functional/immutable-data
|
|
151
|
+
isButtonVisible.value = withSpring(0, IOSpringValues.button);
|
|
152
|
+
const targetY = Math.max(0, contentHeight.value - scrollViewHeight.value);
|
|
153
|
+
scrollTo(scrollViewRef, 0, targetY, true);
|
|
154
|
+
})();
|
|
155
|
+
}, [scrollViewRef, contentHeight, scrollViewHeight, isButtonVisible]);
|
|
168
156
|
|
|
169
157
|
/**
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* (`scrollEnabled` is `true`).
|
|
158
|
+
* The "scroll to bottom" button component. It is wrapped in a reanimated View
|
|
159
|
+
* and has animated style applied to it.
|
|
173
160
|
*/
|
|
174
|
-
const needsScroll = useMemo(
|
|
175
|
-
() =>
|
|
176
|
-
scrollViewHeight > 0 &&
|
|
177
|
-
contentHeight > 0 &&
|
|
178
|
-
scrollViewHeight < contentHeight,
|
|
179
|
-
[scrollViewHeight, contentHeight]
|
|
180
|
-
);
|
|
181
161
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const shouldRenderScrollButton =
|
|
187
|
-
scrollEnabled && needsScroll && isButtonVisible;
|
|
162
|
+
const buttonTransitionStyle = useAnimatedStyle(() => ({
|
|
163
|
+
opacity: isButtonVisible.value,
|
|
164
|
+
transform: [{ scale: interpolate(isButtonVisible.value, [0, 1], [0.5, 1]) }]
|
|
165
|
+
}));
|
|
188
166
|
|
|
189
|
-
/**
|
|
190
|
-
* The "scroll to bottom" button component. It is wrapped in a reanimated view and has enter and exit
|
|
191
|
-
* animations applied to it.
|
|
192
|
-
*/
|
|
193
167
|
const scrollDownButton = (
|
|
194
|
-
<
|
|
195
|
-
springConfig={IOSpringValues.button}
|
|
196
|
-
style={styles.scrollDownButton}
|
|
197
|
-
visible={shouldRenderScrollButton}
|
|
198
|
-
>
|
|
168
|
+
<Animated.View style={[styles.scrollDownButton, buttonTransitionStyle]}>
|
|
199
169
|
<IconButtonSolid
|
|
200
170
|
testID={"ScrollDownButton"}
|
|
201
171
|
accessibilityLabel="Scroll to bottom"
|
|
202
172
|
icon="arrowBottom"
|
|
203
173
|
onPress={handleScrollDownPress}
|
|
204
174
|
/>
|
|
205
|
-
</
|
|
175
|
+
</Animated.View>
|
|
206
176
|
);
|
|
207
177
|
|
|
208
178
|
return (
|
|
209
179
|
<>
|
|
210
|
-
<ScrollView
|
|
180
|
+
<Animated.ScrollView
|
|
211
181
|
testID={"ScrollView"}
|
|
212
182
|
ref={scrollViewRef}
|
|
213
183
|
scrollEnabled={scrollEnabled}
|
|
214
184
|
style={style}
|
|
215
|
-
onScroll={handleScroll}
|
|
216
|
-
scrollEventThrottle={8}
|
|
217
185
|
onLayout={handleLayout}
|
|
218
186
|
onContentSizeChange={handleContentSizeChange}
|
|
219
187
|
contentContainerStyle={contentContainerStyle}
|
|
@@ -226,7 +194,7 @@ const ForceScrollDownView = ({
|
|
|
226
194
|
fixed={false}
|
|
227
195
|
/>
|
|
228
196
|
)}
|
|
229
|
-
</ScrollView>
|
|
197
|
+
</Animated.ScrollView>
|
|
230
198
|
{scrollDownButton}
|
|
231
199
|
</>
|
|
232
200
|
);
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _reactNative = require("@testing-library/react-native");
|
|
4
|
-
var _react = _interopRequireDefault(require("react"));
|
|
5
|
-
var _reactNative2 = require("react-native");
|
|
6
|
-
var _ForceScrollDownView = require("../ForceScrollDownView");
|
|
7
|
-
var _jsxRuntime = require("react/jsx-runtime");
|
|
8
|
-
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
-
/* eslint-disable functional/immutable-data */
|
|
10
|
-
|
|
11
|
-
const tContent = "Some content";
|
|
12
|
-
describe("ForceScrollDownView", () => {
|
|
13
|
-
jest.useFakeTimers();
|
|
14
|
-
it("should match snapshot", () => {
|
|
15
|
-
const tChildren = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative2.Text, {
|
|
16
|
-
children: tContent
|
|
17
|
-
});
|
|
18
|
-
const component = (0, _reactNative.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_ForceScrollDownView.ForceScrollDownView, {
|
|
19
|
-
threshold: 100,
|
|
20
|
-
children: tChildren
|
|
21
|
-
}));
|
|
22
|
-
expect(component).toMatchSnapshot();
|
|
23
|
-
});
|
|
24
|
-
it("renders the content correctly", () => {
|
|
25
|
-
const tChildren = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative2.Text, {
|
|
26
|
-
children: tContent
|
|
27
|
-
});
|
|
28
|
-
const {
|
|
29
|
-
getByText
|
|
30
|
-
} = (0, _reactNative.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_ForceScrollDownView.ForceScrollDownView, {
|
|
31
|
-
threshold: 100,
|
|
32
|
-
children: tChildren
|
|
33
|
-
}));
|
|
34
|
-
expect(getByText(tContent)).toBeDefined();
|
|
35
|
-
});
|
|
36
|
-
it("displays the scroll down button when necessary", async () => {
|
|
37
|
-
const tChildren = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative2.Text, {
|
|
38
|
-
children: tContent
|
|
39
|
-
});
|
|
40
|
-
const tScreenHeight = 1000;
|
|
41
|
-
const {
|
|
42
|
-
getByTestId,
|
|
43
|
-
queryByTestId
|
|
44
|
-
} = (0, _reactNative.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_ForceScrollDownView.ForceScrollDownView, {
|
|
45
|
-
threshold: 100,
|
|
46
|
-
children: tChildren
|
|
47
|
-
}));
|
|
48
|
-
const scrollView = getByTestId("ScrollView");
|
|
49
|
-
|
|
50
|
-
// Update scroll view height
|
|
51
|
-
(0, _reactNative.fireEvent)(scrollView, "layout", {
|
|
52
|
-
nativeEvent: {
|
|
53
|
-
layout: {
|
|
54
|
-
height: tScreenHeight
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
// Update scroll view content height
|
|
60
|
-
(0, _reactNative.fireEvent)(scrollView, "contentSizeChange", null, tScreenHeight - 500);
|
|
61
|
-
|
|
62
|
-
// Button should not be visible because content does not need scrolling
|
|
63
|
-
const buttonBefore = queryByTestId("ScrollDownButton");
|
|
64
|
-
expect(buttonBefore).toBeNull();
|
|
65
|
-
|
|
66
|
-
// Increase content height to force button to be shown
|
|
67
|
-
(0, _reactNative.fireEvent)(scrollView, "contentSizeChange", null, tScreenHeight + 500);
|
|
68
|
-
jest.advanceTimersByTime(500);
|
|
69
|
-
|
|
70
|
-
// Button should be visible now beacuse content needs scrolling
|
|
71
|
-
const buttonAfter = queryByTestId("ScrollDownButton");
|
|
72
|
-
expect(buttonAfter).not.toBeNull();
|
|
73
|
-
});
|
|
74
|
-
it("scrolls to the bottom when the button is pressed", () => {
|
|
75
|
-
const tChildren = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative2.Text, {
|
|
76
|
-
children: tContent
|
|
77
|
-
});
|
|
78
|
-
const tScreenHeight = 1000;
|
|
79
|
-
const {
|
|
80
|
-
getByTestId,
|
|
81
|
-
queryByTestId
|
|
82
|
-
} = (0, _reactNative.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_ForceScrollDownView.ForceScrollDownView, {
|
|
83
|
-
threshold: 100,
|
|
84
|
-
children: tChildren
|
|
85
|
-
}));
|
|
86
|
-
const scrollView = getByTestId("ScrollView");
|
|
87
|
-
|
|
88
|
-
// Update scroll view height
|
|
89
|
-
(0, _reactNative.fireEvent)(scrollView, "layout", {
|
|
90
|
-
nativeEvent: {
|
|
91
|
-
layout: {
|
|
92
|
-
height: tScreenHeight
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Update scroll view content height
|
|
98
|
-
(0, _reactNative.fireEvent)(scrollView, "contentSizeChange", null, tScreenHeight + 500);
|
|
99
|
-
|
|
100
|
-
// Button should be visible
|
|
101
|
-
const buttonBefore = getByTestId("ScrollDownButton");
|
|
102
|
-
expect(buttonBefore).not.toBeNull();
|
|
103
|
-
|
|
104
|
-
// Fire button press event
|
|
105
|
-
_reactNative.fireEvent.press(buttonBefore);
|
|
106
|
-
|
|
107
|
-
// Wait for the scroll animation
|
|
108
|
-
jest.advanceTimersByTime(500);
|
|
109
|
-
|
|
110
|
-
// Button should not be visible after scrolling
|
|
111
|
-
const buttonAfter = queryByTestId("ScrollDownButton");
|
|
112
|
-
expect(buttonAfter).toBeNull();
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
//# sourceMappingURL=ForceScrollDownView.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","_react","_interopRequireDefault","_reactNative2","_ForceScrollDownView","_jsxRuntime","e","__esModule","default","tContent","describe","jest","useFakeTimers","it","tChildren","jsx","Text","children","component","render","ForceScrollDownView","threshold","expect","toMatchSnapshot","getByText","toBeDefined","tScreenHeight","getByTestId","queryByTestId","scrollView","fireEvent","nativeEvent","layout","height","buttonBefore","toBeNull","advanceTimersByTime","buttonAfter","not","press"],"sourceRoot":"../../../../../src","sources":["components/templates/__test__/ForceScrollDownView.test.tsx"],"mappings":"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","ignoreList":[]}
|
package/lib/commonjs/components/templates/__test__/__snapshots__/ForceScrollDownView.test.tsx.snap
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`ForceScrollDownView should match snapshot 1`] = `
|
|
4
|
-
<RCTScrollView
|
|
5
|
-
onContentSizeChange={[Function]}
|
|
6
|
-
onLayout={[Function]}
|
|
7
|
-
onScroll={[Function]}
|
|
8
|
-
scrollEnabled={true}
|
|
9
|
-
scrollEventThrottle={8}
|
|
10
|
-
testID="ScrollView"
|
|
11
|
-
>
|
|
12
|
-
<View>
|
|
13
|
-
<Text>
|
|
14
|
-
Some content
|
|
15
|
-
</Text>
|
|
16
|
-
</View>
|
|
17
|
-
</RCTScrollView>
|
|
18
|
-
`;
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/* eslint-disable functional/immutable-data */
|
|
4
|
-
import { fireEvent, render } from "@testing-library/react-native";
|
|
5
|
-
import React from "react";
|
|
6
|
-
import { Text } from "react-native";
|
|
7
|
-
import { ForceScrollDownView } from "../ForceScrollDownView";
|
|
8
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
|
-
const tContent = "Some content";
|
|
10
|
-
describe("ForceScrollDownView", () => {
|
|
11
|
-
jest.useFakeTimers();
|
|
12
|
-
it("should match snapshot", () => {
|
|
13
|
-
const tChildren = /*#__PURE__*/_jsx(Text, {
|
|
14
|
-
children: tContent
|
|
15
|
-
});
|
|
16
|
-
const component = render(/*#__PURE__*/_jsx(ForceScrollDownView, {
|
|
17
|
-
threshold: 100,
|
|
18
|
-
children: tChildren
|
|
19
|
-
}));
|
|
20
|
-
expect(component).toMatchSnapshot();
|
|
21
|
-
});
|
|
22
|
-
it("renders the content correctly", () => {
|
|
23
|
-
const tChildren = /*#__PURE__*/_jsx(Text, {
|
|
24
|
-
children: tContent
|
|
25
|
-
});
|
|
26
|
-
const {
|
|
27
|
-
getByText
|
|
28
|
-
} = render(/*#__PURE__*/_jsx(ForceScrollDownView, {
|
|
29
|
-
threshold: 100,
|
|
30
|
-
children: tChildren
|
|
31
|
-
}));
|
|
32
|
-
expect(getByText(tContent)).toBeDefined();
|
|
33
|
-
});
|
|
34
|
-
it("displays the scroll down button when necessary", async () => {
|
|
35
|
-
const tChildren = /*#__PURE__*/_jsx(Text, {
|
|
36
|
-
children: tContent
|
|
37
|
-
});
|
|
38
|
-
const tScreenHeight = 1000;
|
|
39
|
-
const {
|
|
40
|
-
getByTestId,
|
|
41
|
-
queryByTestId
|
|
42
|
-
} = render(/*#__PURE__*/_jsx(ForceScrollDownView, {
|
|
43
|
-
threshold: 100,
|
|
44
|
-
children: tChildren
|
|
45
|
-
}));
|
|
46
|
-
const scrollView = getByTestId("ScrollView");
|
|
47
|
-
|
|
48
|
-
// Update scroll view height
|
|
49
|
-
fireEvent(scrollView, "layout", {
|
|
50
|
-
nativeEvent: {
|
|
51
|
-
layout: {
|
|
52
|
-
height: tScreenHeight
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// Update scroll view content height
|
|
58
|
-
fireEvent(scrollView, "contentSizeChange", null, tScreenHeight - 500);
|
|
59
|
-
|
|
60
|
-
// Button should not be visible because content does not need scrolling
|
|
61
|
-
const buttonBefore = queryByTestId("ScrollDownButton");
|
|
62
|
-
expect(buttonBefore).toBeNull();
|
|
63
|
-
|
|
64
|
-
// Increase content height to force button to be shown
|
|
65
|
-
fireEvent(scrollView, "contentSizeChange", null, tScreenHeight + 500);
|
|
66
|
-
jest.advanceTimersByTime(500);
|
|
67
|
-
|
|
68
|
-
// Button should be visible now beacuse content needs scrolling
|
|
69
|
-
const buttonAfter = queryByTestId("ScrollDownButton");
|
|
70
|
-
expect(buttonAfter).not.toBeNull();
|
|
71
|
-
});
|
|
72
|
-
it("scrolls to the bottom when the button is pressed", () => {
|
|
73
|
-
const tChildren = /*#__PURE__*/_jsx(Text, {
|
|
74
|
-
children: tContent
|
|
75
|
-
});
|
|
76
|
-
const tScreenHeight = 1000;
|
|
77
|
-
const {
|
|
78
|
-
getByTestId,
|
|
79
|
-
queryByTestId
|
|
80
|
-
} = render(/*#__PURE__*/_jsx(ForceScrollDownView, {
|
|
81
|
-
threshold: 100,
|
|
82
|
-
children: tChildren
|
|
83
|
-
}));
|
|
84
|
-
const scrollView = getByTestId("ScrollView");
|
|
85
|
-
|
|
86
|
-
// Update scroll view height
|
|
87
|
-
fireEvent(scrollView, "layout", {
|
|
88
|
-
nativeEvent: {
|
|
89
|
-
layout: {
|
|
90
|
-
height: tScreenHeight
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Update scroll view content height
|
|
96
|
-
fireEvent(scrollView, "contentSizeChange", null, tScreenHeight + 500);
|
|
97
|
-
|
|
98
|
-
// Button should be visible
|
|
99
|
-
const buttonBefore = getByTestId("ScrollDownButton");
|
|
100
|
-
expect(buttonBefore).not.toBeNull();
|
|
101
|
-
|
|
102
|
-
// Fire button press event
|
|
103
|
-
fireEvent.press(buttonBefore);
|
|
104
|
-
|
|
105
|
-
// Wait for the scroll animation
|
|
106
|
-
jest.advanceTimersByTime(500);
|
|
107
|
-
|
|
108
|
-
// Button should not be visible after scrolling
|
|
109
|
-
const buttonAfter = queryByTestId("ScrollDownButton");
|
|
110
|
-
expect(buttonAfter).toBeNull();
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
//# sourceMappingURL=ForceScrollDownView.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["fireEvent","render","React","Text","ForceScrollDownView","jsx","_jsx","tContent","describe","jest","useFakeTimers","it","tChildren","children","component","threshold","expect","toMatchSnapshot","getByText","toBeDefined","tScreenHeight","getByTestId","queryByTestId","scrollView","nativeEvent","layout","height","buttonBefore","toBeNull","advanceTimersByTime","buttonAfter","not","press"],"sourceRoot":"../../../../../src","sources":["components/templates/__test__/ForceScrollDownView.test.tsx"],"mappings":"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","ignoreList":[]}
|
package/lib/module/components/templates/__test__/__snapshots__/ForceScrollDownView.test.tsx.snap
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`ForceScrollDownView should match snapshot 1`] = `
|
|
4
|
-
<RCTScrollView
|
|
5
|
-
onContentSizeChange={[Function]}
|
|
6
|
-
onLayout={[Function]}
|
|
7
|
-
onScroll={[Function]}
|
|
8
|
-
scrollEnabled={true}
|
|
9
|
-
scrollEventThrottle={8}
|
|
10
|
-
testID="ScrollView"
|
|
11
|
-
>
|
|
12
|
-
<View>
|
|
13
|
-
<Text>
|
|
14
|
-
Some content
|
|
15
|
-
</Text>
|
|
16
|
-
</View>
|
|
17
|
-
</RCTScrollView>
|
|
18
|
-
`;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ForceScrollDownView.test.d.ts","sourceRoot":"","sources":["../../../../../src/components/templates/__test__/ForceScrollDownView.test.tsx"],"names":[],"mappings":""}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/* eslint-disable functional/immutable-data */
|
|
2
|
-
import { fireEvent, render } from "@testing-library/react-native";
|
|
3
|
-
import React from "react";
|
|
4
|
-
import { Text } from "react-native";
|
|
5
|
-
import { ForceScrollDownView } from "../ForceScrollDownView";
|
|
6
|
-
|
|
7
|
-
const tContent = "Some content";
|
|
8
|
-
|
|
9
|
-
describe("ForceScrollDownView", () => {
|
|
10
|
-
jest.useFakeTimers();
|
|
11
|
-
|
|
12
|
-
it("should match snapshot", () => {
|
|
13
|
-
const tChildren = <Text>{tContent}</Text>;
|
|
14
|
-
|
|
15
|
-
const component = render(
|
|
16
|
-
<ForceScrollDownView threshold={100}>{tChildren}</ForceScrollDownView>
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
expect(component).toMatchSnapshot();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("renders the content correctly", () => {
|
|
23
|
-
const tChildren = <Text>{tContent}</Text>;
|
|
24
|
-
|
|
25
|
-
const { getByText } = render(
|
|
26
|
-
<ForceScrollDownView threshold={100}>{tChildren}</ForceScrollDownView>
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
expect(getByText(tContent)).toBeDefined();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("displays the scroll down button when necessary", async () => {
|
|
33
|
-
const tChildren = <Text>{tContent}</Text>;
|
|
34
|
-
|
|
35
|
-
const tScreenHeight = 1000;
|
|
36
|
-
|
|
37
|
-
const { getByTestId, queryByTestId } = render(
|
|
38
|
-
<ForceScrollDownView threshold={100}>{tChildren}</ForceScrollDownView>
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
const scrollView = getByTestId("ScrollView");
|
|
42
|
-
|
|
43
|
-
// Update scroll view height
|
|
44
|
-
fireEvent(scrollView, "layout", {
|
|
45
|
-
nativeEvent: {
|
|
46
|
-
layout: {
|
|
47
|
-
height: tScreenHeight
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Update scroll view content height
|
|
53
|
-
fireEvent(scrollView, "contentSizeChange", null, tScreenHeight - 500);
|
|
54
|
-
|
|
55
|
-
// Button should not be visible because content does not need scrolling
|
|
56
|
-
const buttonBefore = queryByTestId("ScrollDownButton");
|
|
57
|
-
expect(buttonBefore).toBeNull();
|
|
58
|
-
|
|
59
|
-
// Increase content height to force button to be shown
|
|
60
|
-
fireEvent(scrollView, "contentSizeChange", null, tScreenHeight + 500);
|
|
61
|
-
|
|
62
|
-
jest.advanceTimersByTime(500);
|
|
63
|
-
|
|
64
|
-
// Button should be visible now beacuse content needs scrolling
|
|
65
|
-
const buttonAfter = queryByTestId("ScrollDownButton");
|
|
66
|
-
expect(buttonAfter).not.toBeNull();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("scrolls to the bottom when the button is pressed", () => {
|
|
70
|
-
const tChildren = <Text>{tContent}</Text>;
|
|
71
|
-
|
|
72
|
-
const tScreenHeight = 1000;
|
|
73
|
-
|
|
74
|
-
const { getByTestId, queryByTestId } = render(
|
|
75
|
-
<ForceScrollDownView threshold={100}>{tChildren}</ForceScrollDownView>
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
const scrollView = getByTestId("ScrollView");
|
|
79
|
-
|
|
80
|
-
// Update scroll view height
|
|
81
|
-
fireEvent(scrollView, "layout", {
|
|
82
|
-
nativeEvent: {
|
|
83
|
-
layout: {
|
|
84
|
-
height: tScreenHeight
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Update scroll view content height
|
|
90
|
-
fireEvent(scrollView, "contentSizeChange", null, tScreenHeight + 500);
|
|
91
|
-
|
|
92
|
-
// Button should be visible
|
|
93
|
-
const buttonBefore = getByTestId("ScrollDownButton");
|
|
94
|
-
expect(buttonBefore).not.toBeNull();
|
|
95
|
-
|
|
96
|
-
// Fire button press event
|
|
97
|
-
fireEvent.press(buttonBefore);
|
|
98
|
-
|
|
99
|
-
// Wait for the scroll animation
|
|
100
|
-
jest.advanceTimersByTime(500);
|
|
101
|
-
|
|
102
|
-
// Button should not be visible after scrolling
|
|
103
|
-
const buttonAfter = queryByTestId("ScrollDownButton");
|
|
104
|
-
expect(buttonAfter).toBeNull();
|
|
105
|
-
});
|
|
106
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`ForceScrollDownView should match snapshot 1`] = `
|
|
4
|
-
<RCTScrollView
|
|
5
|
-
onContentSizeChange={[Function]}
|
|
6
|
-
onLayout={[Function]}
|
|
7
|
-
onScroll={[Function]}
|
|
8
|
-
scrollEnabled={true}
|
|
9
|
-
scrollEventThrottle={8}
|
|
10
|
-
testID="ScrollView"
|
|
11
|
-
>
|
|
12
|
-
<View>
|
|
13
|
-
<Text>
|
|
14
|
-
Some content
|
|
15
|
-
</Text>
|
|
16
|
-
</View>
|
|
17
|
-
</RCTScrollView>
|
|
18
|
-
`;
|