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-173] Refactoring MultiImage and adding placeholder to Avatar #119

Merged
merged 12 commits into from
Oct 27, 2023
Merged
44 changes: 29 additions & 15 deletions example/src/pages/Logos.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ export const Logos = () => {
const cdnPath = "https://assets.cdn.io.italia.it/logos/organizations/";

const organizationsURIs = [
{
imageSource: undefined,
name: "Placeholder"
},
{
imageSource: `${cdnPath}1199250158.png`,
name: "Comune di Milano"
Expand Down Expand Up @@ -121,6 +125,10 @@ const organizationsURIs = [
{
imageSource: `${cdnPath}80215430580.png`,
name: "Ministero dell'Interno"
},
{
imageSource: `${cdnPath}wrongUri.png`,
name: "Wrong URI"
}
];

Expand All @@ -137,11 +145,13 @@ const renderAvatar = () => (
<Avatar
shape="circle"
size="small"
logoUri={[
{
uri: imageSource
}
]}
logoUri={
imageSource
? {
uri: imageSource
}
: undefined
}
/>
{i < organizationsURIs.length - 1 && <HSpacer size={4} />}
</React.Fragment>
Expand All @@ -159,11 +169,13 @@ const renderAvatar = () => (
<Avatar
shape="square"
size="small"
logoUri={[
{
uri: imageSource
}
]}
logoUri={
imageSource
? {
uri: imageSource
}
: undefined
}
/>
{i < organizationsURIs.length - 1 && <HSpacer size={8} />}
</React.Fragment>
Expand All @@ -181,11 +193,13 @@ const renderAvatar = () => (
<Avatar
shape="square"
size="medium"
logoUri={[
{
uri: imageSource
}
]}
logoUri={
imageSource
? {
uri: imageSource
}
: undefined
}
/>
{i < organizationsURIs.length - 1 && <HSpacer size={8} />}
</React.Fragment>
Expand Down
87 changes: 63 additions & 24 deletions src/components/avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,40 @@
// A component to provide organization logo
import * as React from "react";
import { StyleSheet, View } from "react-native";
import { IOVisualCostants, IOColors, hexToRgba } from "../../core";
import { MultiImage } from "./MultiImage";
import { Image, ImageURISource, StyleSheet, View } from "react-native";
import { Icon } from "../../components/icons";
import {
IOColors,
IOSpacingScale,
IOThemeContext,
IOVisualCostants,
hexToRgba
} from "../../core";
import { addCacheTimestampToUri } from "../../utils/image";

type Avatar = {
shape: "circle" | "square";
size: "small" | "medium";
logoUri: React.ComponentProps<typeof MultiImage>["source"];
logoUri?: ImageURISource;
};

const avatarBorderLightMode = hexToRgba(IOColors.black, 0.1);
const internalSpaceDefaultSize: number = 6;
const internalSpaceLargeSize: number = 9;
const radiusDefaultSize: number = 8;
const internalSpacePlaceholderDefaultSize: IOSpacingScale = 12;
const internalSpacePlaceholderLargeSize: IOSpacingScale = 16;

const dimensionsMap = {
small: {
size: IOVisualCostants.avatarSizeSmall,
internalSpace: internalSpaceDefaultSize,
internalSpacePlaceholder: internalSpacePlaceholderDefaultSize,
radius: radiusDefaultSize
},
medium: {
size: IOVisualCostants.avatarSizeMedium,
internalSpace: internalSpaceLargeSize,
internalSpacePlaceholder: internalSpacePlaceholderLargeSize,
radius: radiusDefaultSize
}
};
Expand All @@ -36,30 +47,58 @@ const styles = StyleSheet.create({
overflow: "hidden",
resizeMode: "contain",
borderColor: avatarBorderLightMode,
borderWidth: 1,
backgroundColor: IOColors.white
borderWidth: 1
},
avatarImage: {
height: "100%",
width: "100%"
}
});

export const Avatar = ({ logoUri, shape, size }: Avatar) => (
<View
style={[
styles.avatarWrapper,
{
height: dimensionsMap[size].size,
width: dimensionsMap[size].size,
borderRadius:
shape === "circle"
? getAvatarCircleShape(size)
: dimensionsMap[size].radius,
padding: dimensionsMap[size].internalSpace
}
]}
>
<MultiImage style={styles.avatarImage} source={logoUri} />
</View>
);
export const Avatar = ({ logoUri, shape, size }: Avatar) => {
const theme = React.useContext(IOThemeContext);

const [imageSource, setImageSource] = React.useState(
!logoUri ? undefined : addCacheTimestampToUri(logoUri)
);

const isPlaceholder = !imageSource;

const onError = () => {
setImageSource(undefined);
};

return (
<View
style={[
styles.avatarWrapper,
{
height: dimensionsMap[size].size,
width: dimensionsMap[size].size,
borderRadius:
shape === "circle"
? getAvatarCircleShape(size)
: dimensionsMap[size].radius,
backgroundColor: isPlaceholder ? IOColors["grey-50"] : IOColors.white,
padding: isPlaceholder
? dimensionsMap[size].internalSpacePlaceholder
: dimensionsMap[size].internalSpace
}
]}
>
{isPlaceholder ? (
<Icon
name="institution"
color={theme["icon-decorative"]}
size={"100%"}
/>
) : (
<Image
source={imageSource}
style={styles.avatarImage}
onError={onError}
/>
)}
</View>
);
};
57 changes: 0 additions & 57 deletions src/components/avatar/MultiImage.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,34 @@ exports[`Test Avatar Components Avatar Snapshot 1`] = `
style={
[
{
"backgroundColor": "#FFFFFF",
"borderColor": "rgba(14,15,19,0.1)",
"borderWidth": 1,
"overflow": "hidden",
"resizeMode": "contain",
},
{
"backgroundColor": "#FFFFFF",
"borderRadius": 22,
"height": 44,
"padding": 6,
"width": 44,
},
]
}
/>
>
<Image
onError={[Function]}
source={
{
"uri": "",
}
}
style={
{
"height": "100%",
"width": "100%",
}
}
/>
</View>
`;

exports[`Test Avatar Components MultiImage Snapshot 1`] = `null`;
9 changes: 1 addition & 8 deletions src/components/avatar/__test__/avatar.test.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
import React from "react";
import * as TestRenderer from "react-test-renderer";
import { Avatar } from "../Avatar";
import { MultiImage } from "../MultiImage";

describe("Test Avatar Components", () => {
it("Avatar Snapshot", () => {
const avatar = TestRenderer.create(
<Avatar shape={"circle"} size={"small"} logoUri={[]}></Avatar>
<Avatar shape={"circle"} size={"small"} logoUri={{ uri: "" }}></Avatar>
).toJSON();
expect(avatar).toMatchSnapshot();
});
it("MultiImage Snapshot", () => {
const multiImage = TestRenderer.create(
<MultiImage source={[]}></MultiImage>
).toJSON();
expect(multiImage).toMatchSnapshot();
});
});
1 change: 0 additions & 1 deletion src/components/avatar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from "./Avatar";
export * from "./MultiImage";
4 changes: 2 additions & 2 deletions src/components/icons/svg/IconInstitution.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { SVGIconProps } from "../Icon";
const IconInstitution = ({ size, style, ...props }: SVGIconProps) => (
<Svg width={size} height={size} viewBox="0 0 24 24" style={style} {...props}>
<Path
fill="currentColor"
fillRule="evenodd"
d="M10.402 2.685a2.997 2.997 0 0 1 3.219 0l7.918 5.04a.999.999 0 0 1 .463.842l-.023 1.423H1.999l.022-1.423c0-.342.174-.66.463-.843l7.918-5.039ZM14.693.999A4.995 4.995 0 0 0 9.33 1L1.41 6.04A2.997 2.997 0 0 0 .024 8.566L0 10.989a1 1 0 0 0 .999 1h1v9.99h-1a.999.999 0 1 0 0 1.998H22.978a1 1 0 0 0 0-1.998h-.999v-9.99h1a1 1 0 0 0 .998-1L24 8.567a2.997 2.997 0 0 0-1.388-2.529L14.693 1ZM5.994 21.98H3.996v-9.99h1.998v9.99Zm9.99-9.99v9.99H7.993v-9.99h7.993Zm3.997 9.99h-1.998v-9.99h1.998v9.99ZM13.51 6.471a1.499 1.499 0 1 1-2.997 0 1.499 1.499 0 0 1 2.997 0Z"
clipRule="evenodd"
d="M10.4023 2.68517c.9818-.62476 2.2365-.62476 3.2182 0l7.9187 5.03918a.999.999 0 0 1 .4627.84286l-.0229 1.42337H1.99809l.02288-1.42337a.99903.99903 0 0 1 .46269-.84286l7.91864-5.03918Zm4.291-1.68572c-1.6363-1.04126-3.7274-1.04126-5.36368 0L1.41093 6.03864C.54641 6.58878.02288 7.54248.02288 8.56721L0 10.9896c0 .5518.44729.9991.99905.9991h.99904v9.9905H.99905c-.55176 0-.99905.4473-.99905.999 0 .5518.44729.9991.99905.9991H22.9781c.5517 0 .999-.4473.999-.9991 0-.5517-.4473-.999-.999-.999h-.9991v-9.9905h.9991c.5517 0 .999-.4473.999-.9991L24 8.56721c0-1.02473-.5235-1.97843-1.388-2.52858L14.6933.99945ZM5.99428 21.9792H3.99619v-9.9905h1.99809v9.9905Zm9.99042-9.9905v9.9905H7.99237v-9.9905h7.99233Zm3.9962 9.9905h-1.9981v-9.9905h1.9981v9.9905ZM13.51 6.47125c0 .82764-.6709 1.49857-1.4986 1.49857-.8276 0-1.4985-.67093-1.4985-1.49857 0-.82764.6709-1.49858 1.4985-1.49858.8277 0 1.4986.67094 1.4986 1.49858Z"
fill="currentColor"
/>
</Svg>
);
Expand Down
9 changes: 2 additions & 7 deletions src/components/icons/svg/originals/IconInstitution.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/listitems/ListItemTransaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const MUNICIPALITY_LOGO_SIZE = 44;

const LeftComponent = ({ logoIcon }: LeftComponentProps) => {
if (isImageUri(logoIcon)) {
return <Avatar logoUri={[logoIcon]} size="small" shape="circle" />;
return <Avatar logoUri={logoIcon} size="small" shape="circle" />;
}
if (React.isValidElement(logoIcon)) {
return <>{logoIcon}</>;
Expand Down
2 changes: 2 additions & 0 deletions src/core/IOColors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ export type IOTheme = {
// Design System related
"cardBorder-default": IOColors;
"icon-default": IOColors;
"icon-decorative": IOColors;
// Layout
"divider-default": IOColors;
// Status
Expand Down Expand Up @@ -312,6 +313,7 @@ export const IOThemeLight: IOTheme = {
// Design System related
"cardBorder-default": "grey-100",
"icon-default": "grey-650",
"icon-decorative": "grey-300",
// Layout
"divider-default": "grey-200",
// Status
Expand Down