@pagopa/io-app-design-system 1.17.0 → 1.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/buttons/ButtonLink.js +14 -0
- package/lib/commonjs/components/buttons/ButtonLink.js.map +1 -1
- package/lib/commonjs/components/codeInput/CodeInput.js +9 -5
- package/lib/commonjs/components/codeInput/CodeInput.js.map +1 -1
- package/lib/commonjs/components/common/ScaleInOutAnimation.js +69 -0
- package/lib/commonjs/components/common/ScaleInOutAnimation.js.map +1 -0
- package/lib/commonjs/components/layout/ForceScrollDownView.js +161 -0
- package/lib/commonjs/components/layout/ForceScrollDownView.js.map +1 -0
- package/lib/commonjs/components/layout/HeaderFirstLevel.js +4 -4
- package/lib/commonjs/components/layout/HeaderFirstLevel.js.map +1 -1
- package/lib/commonjs/components/layout/__test__/ForceScrollDownView.test.js +94 -0
- package/lib/commonjs/components/layout/__test__/ForceScrollDownView.test.js.map +1 -0
- package/lib/commonjs/components/layout/__test__/__snapshots__/ForceScrollDownView.test.tsx.snap +23 -0
- package/lib/commonjs/components/layout/index.js +11 -0
- package/lib/commonjs/components/layout/index.js.map +1 -1
- package/lib/commonjs/components/modules/ModuleCheckout.js.map +1 -1
- package/lib/commonjs/components/numberpad/NumberButton.js +1 -16
- package/lib/commonjs/components/numberpad/NumberButton.js.map +1 -1
- package/lib/commonjs/components/numberpad/NumberPad.js +3 -2
- package/lib/commonjs/components/numberpad/NumberPad.js.map +1 -1
- package/lib/commonjs/components/textInput/TextInputBase.js +1 -0
- package/lib/commonjs/components/textInput/TextInputBase.js.map +1 -1
- package/lib/commonjs/components/toast/ToastProvider.js +17 -16
- package/lib/commonjs/components/toast/ToastProvider.js.map +1 -1
- package/lib/commonjs/core/IOSpacing.js +1 -1
- package/lib/commonjs/core/IOSpacing.js.map +1 -1
- package/lib/commonjs/core/IOStyles.js +3 -1
- package/lib/commonjs/core/IOStyles.js.map +1 -1
- package/lib/module/components/buttons/ButtonLink.js +15 -1
- package/lib/module/components/buttons/ButtonLink.js.map +1 -1
- package/lib/module/components/codeInput/CodeInput.js +9 -5
- package/lib/module/components/codeInput/CodeInput.js.map +1 -1
- package/lib/module/components/common/ScaleInOutAnimation.js +59 -0
- package/lib/module/components/common/ScaleInOutAnimation.js.map +1 -0
- package/lib/module/components/layout/ForceScrollDownView.js +153 -0
- package/lib/module/components/layout/ForceScrollDownView.js.map +1 -0
- package/lib/module/components/layout/HeaderFirstLevel.js +4 -4
- package/lib/module/components/layout/HeaderFirstLevel.js.map +1 -1
- package/lib/module/components/layout/__test__/ForceScrollDownView.test.js +90 -0
- package/lib/module/components/layout/__test__/ForceScrollDownView.test.js.map +1 -0
- package/lib/module/components/layout/__test__/__snapshots__/ForceScrollDownView.test.tsx.snap +23 -0
- package/lib/module/components/layout/index.js +1 -0
- package/lib/module/components/layout/index.js.map +1 -1
- package/lib/module/components/modules/ModuleCheckout.js.map +1 -1
- package/lib/module/components/numberpad/NumberButton.js +2 -17
- package/lib/module/components/numberpad/NumberButton.js.map +1 -1
- package/lib/module/components/numberpad/NumberPad.js +3 -2
- package/lib/module/components/numberpad/NumberPad.js.map +1 -1
- package/lib/module/components/textInput/TextInputBase.js +1 -0
- package/lib/module/components/textInput/TextInputBase.js.map +1 -1
- package/lib/module/components/toast/ToastProvider.js +17 -16
- package/lib/module/components/toast/ToastProvider.js.map +1 -1
- package/lib/module/core/IOSpacing.js +1 -1
- package/lib/module/core/IOSpacing.js.map +1 -1
- package/lib/module/core/IOStyles.js +3 -1
- package/lib/module/core/IOStyles.js.map +1 -1
- package/lib/typescript/components/buttons/ButtonLink.d.ts +1 -1
- package/lib/typescript/components/buttons/ButtonLink.d.ts.map +1 -1
- package/lib/typescript/components/codeInput/CodeInput.d.ts.map +1 -1
- package/lib/typescript/components/common/ScaleInOutAnimation.d.ts +14 -0
- package/lib/typescript/components/common/ScaleInOutAnimation.d.ts.map +1 -0
- package/lib/typescript/components/layout/ForceScrollDownView.d.ts +27 -0
- package/lib/typescript/components/layout/ForceScrollDownView.d.ts.map +1 -0
- package/lib/typescript/components/layout/__test__/ForceScrollDownView.test.d.ts +2 -0
- package/lib/typescript/components/layout/__test__/ForceScrollDownView.test.d.ts.map +1 -0
- package/lib/typescript/components/layout/index.d.ts +1 -0
- package/lib/typescript/components/layout/index.d.ts.map +1 -1
- package/lib/typescript/components/modules/ModuleCheckout.d.ts +12 -6
- package/lib/typescript/components/modules/ModuleCheckout.d.ts.map +1 -1
- package/lib/typescript/components/numberpad/NumberButton.d.ts.map +1 -1
- package/lib/typescript/components/numberpad/NumberPad.d.ts.map +1 -1
- package/lib/typescript/components/textInput/TextInputBase.d.ts.map +1 -1
- package/lib/typescript/components/toast/ToastProvider.d.ts.map +1 -1
- package/lib/typescript/core/IOSpacing.d.ts +1 -1
- package/lib/typescript/core/IOSpacing.d.ts.map +1 -1
- package/lib/typescript/core/IOStyles.d.ts +2 -0
- package/lib/typescript/core/IOStyles.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/components/buttons/ButtonLink.tsx +17 -1
- package/src/components/codeInput/CodeInput.tsx +16 -3
- package/src/components/common/ScaleInOutAnimation.tsx +68 -0
- package/src/components/layout/ForceScrollDownView.tsx +210 -0
- package/src/components/layout/HeaderFirstLevel.tsx +4 -4
- package/src/components/layout/__test__/ForceScrollDownView.test.tsx +106 -0
- package/src/components/layout/__test__/__snapshots__/ForceScrollDownView.test.tsx.snap +23 -0
- package/src/components/layout/index.tsx +1 -0
- package/src/components/modules/ModuleCheckout.tsx +12 -5
- package/src/components/numberpad/NumberButton.tsx +2 -21
- package/src/components/numberpad/NumberPad.tsx +5 -2
- package/src/components/textInput/TextInputBase.tsx +1 -0
- package/src/components/toast/ToastProvider.tsx +12 -17
- package/src/core/IOSpacing.ts +4 -2
- package/src/core/IOStyles.ts +5 -1
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState
|
|
7
|
+
} from "react";
|
|
8
|
+
import {
|
|
9
|
+
LayoutChangeEvent,
|
|
10
|
+
NativeScrollEvent,
|
|
11
|
+
NativeSyntheticEvent,
|
|
12
|
+
ScrollView,
|
|
13
|
+
ScrollViewProps,
|
|
14
|
+
StyleSheet
|
|
15
|
+
} from "react-native";
|
|
16
|
+
import { ScaleInOutAnimation } from "../common/ScaleInOutAnimation";
|
|
17
|
+
import { IOSpringValues, IOVisualCostants } from "../../core";
|
|
18
|
+
import { IconButtonSolid } from "../buttons";
|
|
19
|
+
|
|
20
|
+
type ForceScrollDownViewProps = {
|
|
21
|
+
/**
|
|
22
|
+
* The content to display inside the scroll view.
|
|
23
|
+
*/
|
|
24
|
+
children: React.ReactNode;
|
|
25
|
+
/**
|
|
26
|
+
* The distance from the bottom of the scrollable content at which the "scroll to bottom" button
|
|
27
|
+
* should become hidden. Defaults to 100.
|
|
28
|
+
*/
|
|
29
|
+
threshold?: number;
|
|
30
|
+
/**
|
|
31
|
+
* A callback that will be called whenever the scroll view crosses the threshold. The callback
|
|
32
|
+
* is passed a boolean indicating whether the threshold has been crossed (`true`) or not (`false`).
|
|
33
|
+
*/
|
|
34
|
+
onThresholdCrossed?: (crossed: boolean) => void;
|
|
35
|
+
} & Pick<
|
|
36
|
+
ScrollViewProps,
|
|
37
|
+
"style" | "contentContainerStyle" | "scrollEnabled" | "testID"
|
|
38
|
+
>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A React Native component that displays a scroll view with a button that scrolls to the bottom of the content
|
|
42
|
+
* when pressed. The button is hidden when the scroll view reaches a certain threshold from the bottom, which is
|
|
43
|
+
* configurable by the `threshold` prop. The button, and the scrolling, can also be disabled by setting the
|
|
44
|
+
* `scrollEnabled` prop to `false`.
|
|
45
|
+
*/
|
|
46
|
+
const ForceScrollDownView = ({
|
|
47
|
+
children,
|
|
48
|
+
threshold = 100,
|
|
49
|
+
style,
|
|
50
|
+
contentContainerStyle,
|
|
51
|
+
scrollEnabled = true,
|
|
52
|
+
onThresholdCrossed
|
|
53
|
+
}: ForceScrollDownViewProps) => {
|
|
54
|
+
const scrollViewRef = useRef<ScrollView>(null);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The height of the scroll view, used to determine whether or not the scrollable content fits inside
|
|
58
|
+
* the scroll view and whether the "scroll to bottom" button should be displayed.
|
|
59
|
+
*/
|
|
60
|
+
const [scrollViewHeight, setScrollViewHeight] = useState<number>();
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The height of the scrollable content, used to determine whether or not the "scroll to bottom" button
|
|
64
|
+
* should be displayed.
|
|
65
|
+
*/
|
|
66
|
+
const [contentHeight, setContentHeight] = useState<number>();
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Whether or not the scroll view has crossed the threshold from the bottom.
|
|
70
|
+
*/
|
|
71
|
+
const [isThresholdCrossed, setThresholdCrossed] = useState(false);
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Whether or not the "scroll to bottom" button should be visible. This is controlled by the threshold
|
|
75
|
+
* and the current scroll position.
|
|
76
|
+
*/
|
|
77
|
+
const [isButtonVisible, setButtonVisible] = useState(true);
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* A callback that is called whenever the scroll view is scrolled. It checks whether or not the
|
|
81
|
+
* scroll view has crossed the threshold from the bottom and updates the state accordingly.
|
|
82
|
+
* The callback is designed to updatr button visibility only when crossing the threshold.
|
|
83
|
+
*/
|
|
84
|
+
const handleScroll = useCallback(
|
|
85
|
+
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
86
|
+
const { layoutMeasurement, contentOffset, contentSize } =
|
|
87
|
+
event.nativeEvent;
|
|
88
|
+
|
|
89
|
+
const thresholdCrossed =
|
|
90
|
+
layoutMeasurement.height + contentOffset.y >=
|
|
91
|
+
contentSize.height - threshold;
|
|
92
|
+
|
|
93
|
+
setThresholdCrossed(previousState => {
|
|
94
|
+
if (!previousState && thresholdCrossed) {
|
|
95
|
+
setButtonVisible(false);
|
|
96
|
+
}
|
|
97
|
+
if (previousState && !thresholdCrossed) {
|
|
98
|
+
setButtonVisible(true);
|
|
99
|
+
}
|
|
100
|
+
return thresholdCrossed;
|
|
101
|
+
});
|
|
102
|
+
},
|
|
103
|
+
[threshold]
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* A side effect that calls the `onThresholdCrossed` callback whenever the value of `isThresholdCrossed` changes.
|
|
108
|
+
*/
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
onThresholdCrossed?.(isThresholdCrossed);
|
|
111
|
+
}, [onThresholdCrossed, isThresholdCrossed]);
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* A callback that is called whenever the size of the scrollable content changes. It updates the
|
|
115
|
+
* state with the new content height.
|
|
116
|
+
*/
|
|
117
|
+
const handleContentSizeChange = useCallback(
|
|
118
|
+
(_contentWidth: number, contentHeight: number) => {
|
|
119
|
+
setContentHeight(contentHeight);
|
|
120
|
+
},
|
|
121
|
+
[]
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* A callback that is called whenever the size of the scroll view changes. It updates the state
|
|
126
|
+
* with the new scroll view height.
|
|
127
|
+
*/
|
|
128
|
+
const handleLayout = useCallback((event: LayoutChangeEvent) => {
|
|
129
|
+
setScrollViewHeight(event.nativeEvent.layout.height);
|
|
130
|
+
}, []);
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* A callback that is called when the "scroll to bottom" button is pressed. It scrolls the
|
|
134
|
+
* scroll view to the bottom and hides the button.
|
|
135
|
+
*/
|
|
136
|
+
const handleScrollDownPress = useCallback(() => {
|
|
137
|
+
setButtonVisible(false);
|
|
138
|
+
scrollViewRef.current?.scrollToEnd();
|
|
139
|
+
}, [scrollViewRef]);
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Whether or not the "scroll to bottom" button needs to be displayed. It is only displayed
|
|
143
|
+
* when the scrollable content cannot fit inside the scroll view and the button is enabled
|
|
144
|
+
* (`scrollEnabled` is `true`).
|
|
145
|
+
*/
|
|
146
|
+
const needsScroll = useMemo(
|
|
147
|
+
() =>
|
|
148
|
+
scrollViewHeight != null &&
|
|
149
|
+
contentHeight != null &&
|
|
150
|
+
scrollViewHeight < contentHeight,
|
|
151
|
+
[scrollViewHeight, contentHeight]
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Whether or not to render the "scroll to bottom" button. It is only rendered when the scroll view
|
|
156
|
+
* is enabled, needs to be scrolled, and the button is visible (`isButtonVisible` is `true`).
|
|
157
|
+
*/
|
|
158
|
+
const shouldRenderScrollButton =
|
|
159
|
+
scrollEnabled && needsScroll && isButtonVisible;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The "scroll to bottom" button component. It is wrapped in a reanimated view and has enter and exit
|
|
163
|
+
* animations applied to it.
|
|
164
|
+
*/
|
|
165
|
+
const scrollDownButton = (
|
|
166
|
+
<ScaleInOutAnimation
|
|
167
|
+
springConfig={IOSpringValues.button}
|
|
168
|
+
style={styles.scrollDownButton}
|
|
169
|
+
visible={shouldRenderScrollButton}
|
|
170
|
+
>
|
|
171
|
+
<IconButtonSolid
|
|
172
|
+
testID={"ScrollDownButton"}
|
|
173
|
+
accessibilityLabel="Scroll to bottom"
|
|
174
|
+
icon="arrowBottom"
|
|
175
|
+
onPress={handleScrollDownPress}
|
|
176
|
+
/>
|
|
177
|
+
</ScaleInOutAnimation>
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<>
|
|
182
|
+
<ScrollView
|
|
183
|
+
testID={"ScrollView"}
|
|
184
|
+
ref={scrollViewRef}
|
|
185
|
+
scrollIndicatorInsets={{ right: 1 }}
|
|
186
|
+
scrollEnabled={scrollEnabled}
|
|
187
|
+
onScroll={handleScroll}
|
|
188
|
+
scrollEventThrottle={400}
|
|
189
|
+
style={style}
|
|
190
|
+
onLayout={handleLayout}
|
|
191
|
+
onContentSizeChange={handleContentSizeChange}
|
|
192
|
+
contentContainerStyle={contentContainerStyle}
|
|
193
|
+
>
|
|
194
|
+
{children}
|
|
195
|
+
</ScrollView>
|
|
196
|
+
{scrollDownButton}
|
|
197
|
+
</>
|
|
198
|
+
);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const styles = StyleSheet.create({
|
|
202
|
+
scrollDownButton: {
|
|
203
|
+
position: "absolute",
|
|
204
|
+
zIndex: 10,
|
|
205
|
+
right: IOVisualCostants.scrollDownButtonRight,
|
|
206
|
+
bottom: IOVisualCostants.scrollDownButtonBottom
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
export { ForceScrollDownView };
|
|
@@ -85,7 +85,7 @@ export const HeaderFirstLevel = ({
|
|
|
85
85
|
<H3
|
|
86
86
|
style={{ flexShrink: 1 }}
|
|
87
87
|
numberOfLines={1}
|
|
88
|
-
color={backgroundColor === "dark" ? "white" :
|
|
88
|
+
color={backgroundColor === "dark" ? "white" : "black"}
|
|
89
89
|
>
|
|
90
90
|
{title}
|
|
91
91
|
</H3>
|
|
@@ -94,7 +94,7 @@ export const HeaderFirstLevel = ({
|
|
|
94
94
|
<>
|
|
95
95
|
<IconButton
|
|
96
96
|
{...thirdAction}
|
|
97
|
-
color={backgroundColor === "dark" ? "contrast" : "
|
|
97
|
+
color={backgroundColor === "dark" ? "contrast" : "primary"}
|
|
98
98
|
/>
|
|
99
99
|
{/* Ideally, with the "gap" flex property,
|
|
100
100
|
we can get rid of these ugly constructs */}
|
|
@@ -105,7 +105,7 @@ export const HeaderFirstLevel = ({
|
|
|
105
105
|
<>
|
|
106
106
|
<IconButton
|
|
107
107
|
{...secondAction}
|
|
108
|
-
color={backgroundColor === "dark" ? "contrast" : "
|
|
108
|
+
color={backgroundColor === "dark" ? "contrast" : "primary"}
|
|
109
109
|
/>
|
|
110
110
|
{/* Same as above */}
|
|
111
111
|
<HSpacer size={16} />
|
|
@@ -114,7 +114,7 @@ export const HeaderFirstLevel = ({
|
|
|
114
114
|
{type !== "base" && (
|
|
115
115
|
<IconButton
|
|
116
116
|
{...firstAction}
|
|
117
|
-
color={backgroundColor === "dark" ? "contrast" : "
|
|
117
|
+
color={backgroundColor === "dark" ? "contrast" : "primary"}
|
|
118
118
|
/>
|
|
119
119
|
)}
|
|
120
120
|
</View>
|
|
@@ -0,0 +1,106 @@
|
|
|
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>{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>{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>{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>{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
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
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={400}
|
|
10
|
+
scrollIndicatorInsets={
|
|
11
|
+
{
|
|
12
|
+
"right": 1,
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
testID="ScrollView"
|
|
16
|
+
>
|
|
17
|
+
<View>
|
|
18
|
+
<Text>
|
|
19
|
+
Some content
|
|
20
|
+
</Text>
|
|
21
|
+
</View>
|
|
22
|
+
</RCTScrollView>
|
|
23
|
+
`;
|
|
@@ -10,18 +10,25 @@ import { PressableModuleBase } from "./PressableModuleBase";
|
|
|
10
10
|
|
|
11
11
|
// ---------------- types ----------------
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
type ModuleCheckoutPartialProps =
|
|
14
14
|
| {
|
|
15
|
+
isLoading?: false;
|
|
15
16
|
paymentLogo?: IOLogoPaymentType;
|
|
16
17
|
title: string;
|
|
17
18
|
subtitle: string;
|
|
18
|
-
|
|
19
|
-
onPress?: () => void;
|
|
19
|
+
onPress: () => void;
|
|
20
20
|
}
|
|
21
21
|
| {
|
|
22
22
|
isLoading: true;
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
paymentLogo?: never;
|
|
24
|
+
title?: never;
|
|
25
|
+
subtitle?: never;
|
|
26
|
+
onPress?: never;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type ModuleCheckoutProps = {
|
|
30
|
+
ctaText: string;
|
|
31
|
+
} & ModuleCheckoutPartialProps;
|
|
25
32
|
|
|
26
33
|
type CtaOnlyProps = { text?: string };
|
|
27
34
|
|
|
@@ -13,8 +13,7 @@ import {
|
|
|
13
13
|
IOColors,
|
|
14
14
|
IONumberPadButtonStyles,
|
|
15
15
|
IOScaleValues,
|
|
16
|
-
IOSpringValues
|
|
17
|
-
useIOExperimentalDesign
|
|
16
|
+
IOSpringValues
|
|
18
17
|
} from "../../core";
|
|
19
18
|
import { H3 } from "../typography";
|
|
20
19
|
|
|
@@ -45,30 +44,12 @@ const colorMap: Record<NumberButtonVariantType, ColorMapVariant> = {
|
|
|
45
44
|
}
|
|
46
45
|
};
|
|
47
46
|
|
|
48
|
-
const legacyColorMap: Record<NumberButtonVariantType, ColorMapVariant> = {
|
|
49
|
-
light: {
|
|
50
|
-
background: "grey-50",
|
|
51
|
-
pressed: "grey-200",
|
|
52
|
-
foreground: "blue"
|
|
53
|
-
},
|
|
54
|
-
dark: {
|
|
55
|
-
background: "blue",
|
|
56
|
-
pressed: "blue-600",
|
|
57
|
-
foreground: "white"
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
47
|
export const NumberButton = ({
|
|
62
48
|
number,
|
|
63
49
|
variant,
|
|
64
50
|
onPress
|
|
65
51
|
}: NumberButtonProps) => {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
const colors = useMemo(
|
|
69
|
-
() => (isExperimental ? colorMap[variant] : legacyColorMap[variant]),
|
|
70
|
-
[variant, isExperimental]
|
|
71
|
-
);
|
|
52
|
+
const colors = useMemo(() => colorMap[variant], [variant]);
|
|
72
53
|
const isPressed = useSharedValue(0);
|
|
73
54
|
// Scaling transformation applied when the button is pressed
|
|
74
55
|
const animationScaleValue = IOScaleValues?.basicButton?.pressedState;
|
|
@@ -74,9 +74,12 @@ export const NumberPad = ({
|
|
|
74
74
|
}) => (
|
|
75
75
|
<View
|
|
76
76
|
style={[
|
|
77
|
-
IOStyles.flex,
|
|
78
77
|
IOStyles.rowSpaceBetween,
|
|
79
|
-
{
|
|
78
|
+
{
|
|
79
|
+
justifyContent: "space-between",
|
|
80
|
+
alignItems: "center",
|
|
81
|
+
flexGrow: 1
|
|
82
|
+
}
|
|
80
83
|
]}
|
|
81
84
|
>
|
|
82
85
|
{buttons.map(elem => {
|
|
@@ -7,12 +7,12 @@ import Animated, {
|
|
|
7
7
|
SlideInUp,
|
|
8
8
|
SlideOutUp
|
|
9
9
|
} from "react-native-reanimated";
|
|
10
|
-
import { Dismissable } from "../layout/Dismissable";
|
|
11
10
|
import { triggerHaptic } from "../../functions";
|
|
11
|
+
import { Dismissable } from "../layout/Dismissable";
|
|
12
12
|
import { ToastNotification } from "./ToastNotification";
|
|
13
|
+
import { ToastContext } from "./context";
|
|
13
14
|
import { Toast } from "./types";
|
|
14
15
|
import { IOToastRef, useIOToast } from "./useIOToast";
|
|
15
|
-
import { ToastContext } from "./context";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* The maximum number of toasts that can be displayed at the same time
|
|
@@ -67,7 +67,16 @@ export const ToastProvider = ({ children }: ToastProviderProps) => {
|
|
|
67
67
|
|
|
68
68
|
const addToast = React.useCallback((toast: Toast): number => {
|
|
69
69
|
const id = (toastId.current ?? 0) + 1;
|
|
70
|
-
|
|
70
|
+
// eslint-disable-next-line functional/immutable-data
|
|
71
|
+
toastId.current = id;
|
|
72
|
+
setToasts(prevToasts => {
|
|
73
|
+
if (prevToasts.length >= MAX_TOAST_STACK_SIZE) {
|
|
74
|
+
const mostRecentToasts = prevToasts.slice(0, -1);
|
|
75
|
+
return [{ id, ...toast }, ...mostRecentToasts];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return [{ id, ...toast }, ...prevToasts];
|
|
79
|
+
});
|
|
71
80
|
|
|
72
81
|
setTimeout(() => {
|
|
73
82
|
setToasts(prevToasts => prevToasts.filter(t => t.id !== id));
|
|
@@ -84,20 +93,6 @@ export const ToastProvider = ({ children }: ToastProviderProps) => {
|
|
|
84
93
|
setToasts(prevToasts => prevToasts.filter(t => t.id !== id));
|
|
85
94
|
}, []);
|
|
86
95
|
|
|
87
|
-
const removeToastAtIndex = (index: number) => {
|
|
88
|
-
setToasts(prevToasts => [
|
|
89
|
-
...prevToasts.slice(0, index),
|
|
90
|
-
...prevToasts.slice(index + 1)
|
|
91
|
-
]);
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// If stack size exceed the maximum, remove the oldest toast
|
|
95
|
-
React.useEffect(() => {
|
|
96
|
-
if (toasts.length > MAX_TOAST_STACK_SIZE) {
|
|
97
|
-
removeToastAtIndex(MAX_TOAST_STACK_SIZE);
|
|
98
|
-
}
|
|
99
|
-
}, [toasts]);
|
|
100
|
-
|
|
101
96
|
const removeAllToasts = React.useCallback(() => {
|
|
102
97
|
setToasts([]);
|
|
103
98
|
}, []);
|
package/src/core/IOSpacing.ts
CHANGED
|
@@ -28,8 +28,10 @@ export const IOSpacer: ReadonlyArray<IOSpacer> = [
|
|
|
28
28
|
] as const;
|
|
29
29
|
|
|
30
30
|
// Margin values used in the new `<ContentWrapper>` component
|
|
31
|
-
export type IOAppMargin = Extract<IOSpacingScale, 8 | 16 | 24 | 32>;
|
|
32
|
-
export const IOAppMargin: ReadonlyArray<IOAppMargin> = [
|
|
31
|
+
export type IOAppMargin = Extract<IOSpacingScale, 8 | 16 | 24 | 32 | 48>;
|
|
32
|
+
export const IOAppMargin: ReadonlyArray<IOAppMargin> = [
|
|
33
|
+
8, 16, 24, 32, 48
|
|
34
|
+
] as const;
|
|
33
35
|
|
|
34
36
|
// Values used in the `<Alert>` component
|
|
35
37
|
export type IOAlertSpacing = Extract<IOSpacingScale, 16 | 24>;
|
package/src/core/IOStyles.ts
CHANGED
|
@@ -22,6 +22,8 @@ interface IOVisualCostants {
|
|
|
22
22
|
avatarSizeSmall: number;
|
|
23
23
|
avatarSizeMedium: number;
|
|
24
24
|
iconContainedSizeDefault: number;
|
|
25
|
+
scrollDownButtonRight: number;
|
|
26
|
+
scrollDownButtonBottom: number;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
export const IOVisualCostants: IOVisualCostants = {
|
|
@@ -29,7 +31,9 @@ export const IOVisualCostants: IOVisualCostants = {
|
|
|
29
31
|
headerHeight: 56,
|
|
30
32
|
avatarSizeSmall: 44,
|
|
31
33
|
avatarSizeMedium: 66,
|
|
32
|
-
iconContainedSizeDefault: 44
|
|
34
|
+
iconContainedSizeDefault: 44,
|
|
35
|
+
scrollDownButtonRight: 24,
|
|
36
|
+
scrollDownButtonBottom: 24
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
export const IOStyles = StyleSheet.create({
|