Skip to content

Commit

Permalink
[IOAPPFD0-172] Add loading state to the ButtonSolid (with custom …
Browse files Browse the repository at this point in the history
…transitions) (#99)
  • Loading branch information
dmnplb authored Oct 10, 2023
1 parent 0ef2d37 commit 47384e4
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 60 deletions.
57 changes: 48 additions & 9 deletions example/src/pages/Buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
VSpacer,
useIOExperimentalDesign
} from "@pagopa/io-app-design-system";
import React from "react";
import React, { useState } from "react";
import { Alert, StyleSheet, View } from "react-native";
import { ComponentViewerBox } from "../components/ComponentViewerBox";
import { Screen } from "../components/Screen";
Expand Down Expand Up @@ -95,14 +95,15 @@ export const Buttons = () => {
</View>
</ComponentViewerBox>
<ComponentViewerBox name="ButtonSolid · Primary, Full width">
<View>
<ButtonSolid
fullWidth
accessibilityLabel="Tap to trigger test alert"
label={"Primary button (full width)"}
onPress={onButtonPress}
/>
</View>
<ButtonSolid
fullWidth
accessibilityLabel="Tap to trigger test alert"
label={"Primary button (full width)"}
onPress={onButtonPress}
/>
</ComponentViewerBox>
<ComponentViewerBox name="ButtonSolid · Primary · Full width, loading state">
<LoadingSolidButtonExample />
</ComponentViewerBox>
<ComponentViewerBox name="ButtonSolid · Primary, disabled">
<View>
Expand Down Expand Up @@ -244,6 +245,22 @@ export const Buttons = () => {
</View>
</ComponentViewerBox>

<ComponentViewerBox
name="ButtonSolid · Contrast, full width, loading state"
colorMode="dark"
>
<View>
<ButtonSolid
fullWidth
loading
color="contrast"
label={"Contrast button"}
onPress={onButtonPress}
accessibilityLabel="Tap to trigger test alert"
/>
</View>
</ComponentViewerBox>

<ComponentViewerBox
name="ButtonSolid · Contrast, disabled"
colorMode="dark"
Expand Down Expand Up @@ -825,3 +842,25 @@ export const Buttons = () => {
</Screen>
);
};

const LoadingSolidButtonExample = () => {
const [isEnabled, setIsEnabled] = useState(false);
const toggleSwitch = () => setIsEnabled(previousState => !previousState);

return (
<View>
<ButtonSolid
fullWidth
loading={isEnabled}
accessibilityLabel="Tap to trigger test alert"
label={"Primary button"}
onPress={() => setIsEnabled(true)}
/>
<ListItemSwitch
label="Abilita lo stato di caricamento"
onSwitchValueChange={toggleSwitch}
value={isEnabled}
/>
</View>
);
};
89 changes: 67 additions & 22 deletions src/components/buttons/ButtonSolid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Animated, {
useSharedValue,
withSpring
} from "react-native-reanimated";
import ReactNativeHapticFeedback from "react-native-haptic-feedback";
import { IOIconSizeScale, IOIcons, Icon } from "../icons";
import { WithTestID } from "../../utils/types";
import { HSpacer } from "../spacer/Spacer";
Expand All @@ -19,8 +20,12 @@ import {
IOColors,
IOScaleValues,
IOSpringValues,
enterTransitionInnerContent,
enterTransitionInnerContentSmall,
exitTransitionInnerContent,
useIOExperimentalDesign
} from "../../core";
import { LoadingSpinner } from "../loadingSpinner";

type ButtonSolidColor = "primary" | "danger" | "contrast";

Expand Down Expand Up @@ -65,6 +70,10 @@ export type ButtonSolidProps = WithTestID<{
* @default false
*/
fullWidth?: boolean;
/**
* @default false
*/
loading?: boolean;
/**
* @default false
*/
Expand Down Expand Up @@ -152,6 +161,7 @@ export const ButtonSolid = React.memo(
label,
fullWidth = false,
disabled = false,
loading = false,
icon,
iconPosition = "start",
onPress,
Expand Down Expand Up @@ -211,60 +221,95 @@ export const ButtonSolid = React.memo(
isPressed.value = 0;
}, [isPressed]);

const handleOnPress = useCallback(
(event: GestureResponderEvent) => {
/* Don't call `onPress` if the button is
in loading state */
if (loading) {
return;
}
ReactNativeHapticFeedback.trigger("impactLight");
onPress(event);
},
[loading, onPress]
);

// Label & Icons colors
const foregroundColor: IOColors = disabled
? colorMap[color]?.label?.disabled
: colorMap[color]?.label?.default;

return (
<Pressable
testID={testID}
accessible={true}
accessibilityLabel={accessibilityLabel}
accessibilityHint={accessibilityHint}
accessibilityState={{ busy: loading }}
accessibilityRole={"button"}
testID={testID}
onPress={onPress}
onPress={handleOnPress}
onPressIn={onPressIn}
onPressOut={onPressOut}
accessible={true}
disabled={disabled}
style={!fullWidth ? IOButtonStyles.dimensionsDefault : {}}
>
<Animated.View
style={[
buttonStyles.button,
{ overflow: "hidden" },
isExperimental && fullWidth && { paddingHorizontal: 16 },
iconPosition === "end" && { flexDirection: "row-reverse" },
buttonStyles.buttonSizeDefault,
disabled
? isExperimental
? styles.backgroundDisabled
: legacyStyles.backgroundDisabled
: { backgroundColor: colorMap[color]?.default },
/* Prevent Reanimated from overriding background colors
if button is disabled */
if button is disabled */
!disabled && pressedAnimationStyle
]}
>
{icon && (
<>
{/* If 'iconPosition' is set to 'end', we use
{loading && (
<Animated.View
style={buttonStyles.buttonInner}
entering={enterTransitionInnerContentSmall}
exiting={exitTransitionInnerContent}
>
<LoadingSpinner color={foregroundColor} />
</Animated.View>
)}

{!loading && (
<Animated.View
style={[
buttonStyles.buttonInner,
iconPosition === "end" && { flexDirection: "row-reverse" }
]}
entering={enterTransitionInnerContent}
exiting={exitTransitionInnerContent}
>
{icon && (
<>
{/* If 'iconPosition' is set to 'end', we use
reverse flex property to invert the position */}
<Icon name={icon} size={iconSize} color={foregroundColor} />
{/* Once we have support for 'gap' property,
<Icon name={icon} size={iconSize} color={foregroundColor} />
{/* Once we have support for 'gap' property,
we can get rid of that spacer */}
<HSpacer size={8} />
</>
<HSpacer size={8} />
</>
)}
<ButtonText
color={foregroundColor}
style={IOButtonStyles.label}
numberOfLines={1}
ellipsizeMode="tail"
allowFontScaling={isExperimental}
maxFontSizeMultiplier={1.3}
>
{label}
</ButtonText>
</Animated.View>
)}
<ButtonText
color={foregroundColor}
style={IOButtonStyles.label}
numberOfLines={1}
ellipsizeMode="tail"
allowFontScaling={isExperimental}
maxFontSizeMultiplier={1.3}
>
{label}
</ButtonText>
</Animated.View>
</Pressable>
);
Expand Down
75 changes: 46 additions & 29 deletions src/components/buttons/__test__/__snapshots__/button.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ exports[`Test Buttons Components ButtonSolid Snapshot 1`] = `
accessibilityRole="button"
accessibilityState={
{
"busy": undefined,
"busy": false,
"checked": undefined,
"disabled": false,
"expanded": undefined,
Expand Down Expand Up @@ -416,7 +416,9 @@ exports[`Test Buttons Components ButtonSolid Snapshot 1`] = `
"paddingHorizontal": 16,
"textAlignVertical": "center",
},
false,
{
"overflow": "hidden",
},
false,
{
"height": 40,
Expand All @@ -435,40 +437,55 @@ exports[`Test Buttons Components ButtonSolid Snapshot 1`] = `
]
}
>
<Text
allowFontScaling={false}
color="white"
defaultColor="white"
defaultWeight="Bold"
ellipsizeMode="tail"
font="TitilliumWeb"
fontStyle={
{
"fontSize": 16,
}
}
maxFontSizeMultiplier={1.3}
numberOfLines={1}
<View
entering={[Function]}
exiting={[Function]}
style={
[
{
"alignSelf": "center",
},
{
"fontSize": 16,
},
{
"color": "#FFFFFF",
"fontFamily": "Titillium Web",
"fontStyle": "normal",
"fontWeight": "700",
"alignItems": "center",
"flexDirection": "row",
"justifyContent": "center",
},
false,
]
}
weight="Bold"
>
label
</Text>
<Text
allowFontScaling={false}
color="white"
defaultColor="white"
defaultWeight="Bold"
ellipsizeMode="tail"
font="TitilliumWeb"
fontStyle={
{
"fontSize": 16,
}
}
maxFontSizeMultiplier={1.3}
numberOfLines={1}
style={
[
{
"alignSelf": "center",
},
{
"fontSize": 16,
},
{
"color": "#FFFFFF",
"fontFamily": "Titillium Web",
"fontStyle": "normal",
"fontWeight": "700",
},
]
}
weight="Bold"
>
label
</Text>
</View>
</View>
</View>
`;
Expand Down
10 changes: 10 additions & 0 deletions src/core/IOStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ export const IOButtonLegacyStyles = StyleSheet.create({
// paddingHorizontal: PixelRatio.getFontScale() * 16,
// borderRadius: PixelRatio.getFontScale() * 8
},
buttonInner: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center"
},
/* Labels */
label: {
alignSelf: "center"
Expand Down Expand Up @@ -152,6 +157,11 @@ export const IOButtonStyles = StyleSheet.create({
// paddingHorizontal: PixelRatio.getFontScale() * 16,
// borderRadius: PixelRatio.getFontScale() * 8
},
buttonInner: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center"
},
buttonLink: {
flexDirection: "row",
alignItems: "center",
Expand Down
Loading

0 comments on commit 47384e4

Please sign in to comment.