Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IOPLT-91] Creating badge and tags showcase #22

Merged
merged 12 commits into from
Jul 21, 2023
4 changes: 4 additions & 0 deletions example/src/navigation/navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from "react";
import { Icons } from "../pages/Icons";
import { Logos } from "../pages/Logos";

import { Badges } from "../pages/Badges";
import MainScreen from "../pages/MainScreen";
import { AppParamsList } from "./params";
import APP_ROUTES from "./routes";
Expand All @@ -23,6 +24,9 @@ const AppNavigator = () => (
<Stack.Screen name={APP_ROUTES.FOUNDATION.LOGOS.route} component={Logos} options={{
headerTitle: "Logos", headerBackTitleVisible: false
}} />
<Stack.Screen name={APP_ROUTES.COMPONENTS.BADGE.route} component={Badges} options={{
headerTitle: "Badges & Tags", headerBackTitleVisible: false
}} />
</Stack.Navigator>
);

Expand Down
203 changes: 203 additions & 0 deletions example/src/pages/Badges.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import { Badge, CustomBadge, H2, H4, HSpacer, IOBadge, IOColors, IOStyles, IOTagRadius, PercentageValueBox, Tag, VSpacer } from "@pagopa/io-app-design-system";
import React from "react";
import { StyleSheet, View } from "react-native";
import { ComponentViewerBox } from "../components/ComponentViewerBox";
import { Screen } from "../components/Screen";

const styles = StyleSheet.create({
fakeNavItem: {
aspectRatio: 1,
width: 25,
backgroundColor: IOColors.greyLight
}
});

export const Badges = () => (
<Screen>
<H2 weight={"Bold"} style={{ marginBottom: 16 }}>
Tag
</H2>
{renderTag()}

<VSpacer size={16} />

<H2 weight={"Bold"} style={{ marginVertical: 16 }}>
Badge
</H2>
{renderBadge()}

<VSpacer size={16} />

<H2 weight={"Bold"} style={{ marginVertical: 16 }}>
IOBadge
</H2>
{renderIOBadge()}

<VSpacer size={24} />

<H4 weight="SemiBold" color="bluegreyDark">
DiscountValueBox (CGN)
</H4>
<VSpacer size={16} />
<View style={IOStyles.row}>
<PercentageValueBox value={25} small />
<HSpacer size={16} />
<PercentageValueBox value={25} />
</View>

<VSpacer size={40} />

<H2>Notifications</H2>
<VSpacer size={16} />
<H4 weight="SemiBold" color="bluegreyDark">
CustomBadge
</H4>
<VSpacer size={16} />
<View style={IOStyles.row}>
<View style={styles.fakeNavItem}>
<CustomBadge badgeValue={1} />
</View>
<HSpacer />
<View style={styles.fakeNavItem}>
<CustomBadge badgeValue={99} />
</View>
</View>
</Screen>
);

const renderBadge = () => (
<>
<View style={IOStyles.row}>
<Badge text={"Default"} variant="default" />
</View>
<VSpacer size={16} />
<View style={IOStyles.row}>
<Badge text={"Info"} variant="info" />
<HSpacer size={16} />
<Badge text={"Warning"} variant="warning" />
<HSpacer size={16} />
<Badge text={"Error"} variant="error" />
<HSpacer size={16} />
<Badge text={"Success"} variant="success" />
</View>
<VSpacer size={16} />
<View style={IOStyles.row}>
<Badge text={"Purple"} variant="purple" />
<HSpacer size={16} />
<Badge text={"Light blue"} variant="lightBlue" />
<HSpacer size={16} />
<Badge text={"Blue"} variant="blue" />
<HSpacer size={16} />
<Badge text={"Turquoise"} variant="turquoise" />
<HSpacer size={16} />
</View>
<VSpacer size={16} />
<View
style={{
backgroundColor: IOColors.bluegrey,
padding: 16,
borderRadius: 8
}}
>
<View style={IOStyles.row}>
<Badge text={"Default"} variant="default" />
<HSpacer size={16} />
<Badge text={"Contrast"} variant="contrast" />
</View>
</View>
</>
);

const renderIOBadge = () => (
<>
<View style={IOStyles.row}>
<IOBadge small text={"Badge"} variant="solid" color="blue" />
<HSpacer size={16} />
<IOBadge small text={"Badge"} variant="solid" color="red" />
<HSpacer size={16} />
<IOBadge small text={"Badge"} variant="solid" color="aqua" />
<HSpacer size={16} />
<IOBadge small text={"Badge"} variant="solid" color="grey" />
</View>
<VSpacer size={16} />
<View style={IOStyles.row}>
<IOBadge small text={"Badge"} variant="outline" color="blue" />
<HSpacer size={16} />
<IOBadge small text={"Badge"} variant="outline" color="red" />
<HSpacer size={16} />
<IOBadge small text={"Badge"} variant="outline" color="orange" />
</View>
<VSpacer size={16} />
<View style={IOStyles.row}>
<IOBadge text={"Badge"} variant="solid" color="blue" />
<HSpacer size={16} />
<IOBadge text={"Badge"} variant="solid" color="red" />
<HSpacer size={16} />
<IOBadge text={"Badge"} variant="solid" color="aqua" />
<HSpacer size={16} />
<IOBadge text={"Badge"} variant="solid" color="grey" />
</View>
<VSpacer size={16} />
<View style={IOStyles.row}>
<IOBadge text={"Badge"} variant="outline" color="blue" />
<HSpacer size={16} />
<IOBadge text={"Badge"} variant="outline" color="red" />
<HSpacer size={16} />
<IOBadge text={"Badge"} variant="outline" color="orange" />
</View>
<VSpacer size={16} />
<View
style={{
backgroundColor: IOColors.bluegrey,
padding: 16,
borderRadius: 8
}}
>
<View style={IOStyles.row}>
<IOBadge small text={"Badge"} variant="solid" color="aqua" />
<HSpacer size={16} />
<IOBadge small text={"Badge"} variant="solid" color="white" />
<HSpacer size={16} />
<IOBadge small text={"Badge"} variant="outline" color="white" />
</View>
<VSpacer size={16} />
<View style={IOStyles.row}>
<IOBadge text={"Badge"} variant="solid" color="aqua" />
<HSpacer size={16} />
<IOBadge text={"Badge"} variant="solid" color="white" />
<HSpacer size={16} />
<IOBadge text={"Badge"} variant="outline" color="white" />
</View>
</View>
</>
);

const renderTag = () => (
<View>
<ComponentViewerBox name={"Tag, different variants"}>
<Tag text={"Entro il 30 mag"} variant="warning" />
<VSpacer size={8} />
<Tag text={"Completato"} variant="success" />
<VSpacer size={8} />
<Tag text={"Scaduto"} variant="error" />
<VSpacer size={8} />
<View style={IOStyles.row}>
<Tag text={"Certificato"} variant="qrCode" />
<HSpacer size={8} />
<Tag text={"Valore legale"} variant="legalMessage" />
</View>
</ComponentViewerBox>
<ComponentViewerBox name={"Tag, stress test"}>
<View
style={{
backgroundColor: IOColors["error-100"],
padding: 8,
width: "60%",
borderRadius: IOTagRadius + 8
}}
>
<Tag text={"Looooooooong string"} variant="error" />
</View>
</ComponentViewerBox>
</View>
);
95 changes: 95 additions & 0 deletions src/components/badge/CustomBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { memo } from "react";
import { Text, StyleSheet, View } from "react-native";
import { IOColors } from "../../core/IOColors";
import { makeFontStyleObject } from "../../utils/fonts";

// true if the env is in testing phase
export const isTestEnv = process.env.NODE_ENV === "test";

type Props = {
badgeValue?: number;
};

const BADGE_SIZE = 20;
const brandPrimary = IOColors.blue;
const styles = StyleSheet.create({
textStyle: {
color: IOColors.white,
fontSize: 10,
textAlign: "center",
...makeFontStyleObject("Bold")
},
badgeStyle: {
width: BADGE_SIZE,
height: BADGE_SIZE,
borderRadius: BADGE_SIZE / 2,
alignSelf: "flex-start",
justifyContent: "center",
alignContent: "center",
backgroundColor: brandPrimary,
borderColor: IOColors.white,
borderWidth: 2,
position: "absolute",
left: 12,
bottom: 10,
paddingLeft: 0,
paddingRight: 0
}
});

const MAX_BADGE_VALUE = 99;
const multiplierFallback = 1.4;
// map the digits to display within the badge with the relative width multiplier factor
// (the badge displays value below MAX_BADGE_VALUE)
const multiplierMap: Record<number, number> = {
1: 1,
2: 1.2
};
// get the width multiplier relative to the count of digits to display
const getWidthMultiplier = (text: string): number => {
const digits = text.length;
return multiplierMap[digits] ?? multiplierFallback;
};

/**
* A simple round badge to display a positive number
* Display all numbers less or equal of MAX_BADGE_VALUE
* otherwise ${MAX_BADGE_VALUE}"+" will be displayed
*
* if badgeValue is nullish or negative, null element will be returned
*/
export const CustomBadge = (props: Props) => {
const badgeValue = props.badgeValue ?? 0;
if (badgeValue <= 0) {
return null;
}
const badge = `${Math.min(badgeValue, MAX_BADGE_VALUE)}${badgeValue > MAX_BADGE_VALUE ? "+" : ""
}`;
return (
<View
testID={"badgeTestID"}
style={[
styles.badgeStyle,
{ width: styles.badgeStyle.width * getWidthMultiplier(badge) }
]}
>
<Text
style={styles.textStyle}
accessible={false}
importantForAccessibility={"no-hide-descendants"}
>
{badge}
</Text>
</View>
);
};

export default memo(
CustomBadge,
(prev, next) => prev.badgeValue === next.badgeValue
);

// to ensure right code encapsulation we export functions/variables just for tests purposes
export const customBadgeTestable = isTestEnv
? { getWidthMultiplier, styles }
: undefined;
Loading