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

DSD-1521: Add sizeBasedOn prop to Image #1465

Merged
merged 10 commits into from
Dec 6, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Currently, this repo is in Prerelease. When it is released, this project will ad
### Adds

- Adds the `useDSHeading` hook to render a default H2 heading or a custom heading element.
- Adds the `sizeBasedOn` prop to the `Image` component.

### Updates

Expand Down
21 changes: 16 additions & 5 deletions src/components/Image/Image.mdx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { ArgTypes, Canvas, Description, Meta, Source } from "@storybook/blocks";

import { changelogData } from "./imageChangelogData";
import ComponentChangelogTable from "../../utils/ComponentChangelogTable";
import * as ImageStories from "./Image.stories";
import Link from "../Link/Link";

<Meta of={ImageStories} />

# Image

| Component Version | DS Version |
| ----------------- | ---------- |
| Added | `0.0.6` |
| Latest | `2.0.0` |
| Component Version | DS Version |
| ----------------- | ------------ |
| Added | `0.0.6` |
| Latest | `Prerelease` |

## Table of Contents

Expand All @@ -23,6 +24,7 @@ import Link from "../Link/Link";
- {<Link href="#types" target="_self">Types</Link>}
- {<Link href="#html-attributes" target="_self">HTML Attributes</Link>}
- {<Link href="#lazy-loading" target="_self">Lazy Loading</Link>}
- {<Link href="#changelog" target="_self">Changelog</Link>}

## Overview

Expand Down Expand Up @@ -62,7 +64,12 @@ then an `img` element will be rendered with or without wrapper style divs.

Use the `size` prop to set the desired size with the `ImageSizes` enum.

In addition, the `sizeBasedOn` prop can be passed to determine whether the size
of the `Image` is updated based on either the `"height"` or `"width"`. By default,
the `sizeBasedOn` prop is set to `"width"`.

<Canvas of={ImageStories.Sizes} />
<Canvas of={ImageStories.SizesBasedOnHeight} />
jackiequach marked this conversation as resolved.
Show resolved Hide resolved

## Aspect Ratios

Expand Down Expand Up @@ -130,3 +137,7 @@ Resources:
- [Browser-level image lazy-loading for the web](https://web.dev/browser-level-image-lazy-loading/)

<Canvas of={ImageStories.LazyLoading} />

## Changelog

<ComponentChangelogTable changelogData={changelogData} />
143 changes: 71 additions & 72 deletions src/components/Image/Image.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { withDesign } from "storybook-addon-designs";
import Heading from "../Heading/Heading";
import Image, {
imageRatiosArray,
imageSizeBasedOnArray,
imageSizesArray,
imageTypesArray,
} from "./Image";
Expand Down Expand Up @@ -43,6 +44,11 @@ const meta: Meta<typeof Image> = {
options: imageSizesArray,
table: { defaultValue: { summary: "default" } },
},
sizeBasedOn: {
control: { type: "radio" },
options: imageSizeBasedOnArray,
table: { defaultValue: { summary: "width" } },
},
src: {
description:
"The src attribute is required, and contains the path to the image you want to embed.",
Expand All @@ -53,6 +59,66 @@ const meta: Meta<typeof Image> = {
export default meta;
type Story = StoryObj<typeof Image>;

const imageRow = (opts: any = {}) => {
// We'll use this setup function to render all the images in a list item.
// Some images display better with a dark background.
const styles: any = { textAlign: "center" };
const { size = "large", displayValue, sizeBasedOn = "width", id } = opts;
if (sizeBasedOn === "width" && size === "default") {
styles.width = "100%";
} else if (sizeBasedOn === "height" && size === "default") {
styles.width = "100%";
styles.height = "100%";
}

return (
<Box style={styles} key={id}>
<Heading id={id} level="h4" size="heading6" text={size} />
<Image
alt="Alt text"
caption={displayValue}
size={size}
sizeBasedOn={sizeBasedOn}
src="//placekitten.com/400/300"
/>
</Box>
);
};

const allVStack = (items) => {
return <VStack spacing="l">{items}</VStack>;
};

const imageSizeValues = [
{ size: "xxxsmall", display: "32px", id: "ExtraExtraExtraSmall" },
{ size: "xxsmall", display: "64px", id: "ExtraExtraSmall" },
{ size: "xsmall", display: "96px", id: "ExtraSmall" },
{ size: "small", display: "165px", id: "Small" },
{ size: "medium", display: "225px", id: "Medium" },
{ size: "large", display: "360px", id: "Large" },
{ size: "default", display: "100%", id: "Default" },
];

const sizes = [];
const sizesBasedOnHeight = [];

for (const imageValue of imageSizeValues) {
sizes.push(
imageRow({
size: imageValue.size,
displayValue: imageValue.display,
id: imageValue.id,
})
);
sizesBasedOnHeight.push(
imageRow({
size: imageValue.size,
displayValue: imageValue.display,
id: imageValue.id,
jackiequach marked this conversation as resolved.
Show resolved Hide resolved
sizeBasedOn: "height",
})
);
}
/**
* Main Story for the Image component. This must contains the `args`
* and `parameters` properties in this object.
Expand All @@ -70,6 +136,7 @@ export const WithControls: Story = {
credit: "Image credit",
imageType: "default",
size: "medium",
sizeBasedOn: "width",
src: "//placekitten.com/400/300",
},
render: (args) => <Image {...args} />,
Expand Down Expand Up @@ -105,78 +172,10 @@ export const FigureAndFigcaption: Story = {
render: (args) => <Image {...args} />,
};
export const Sizes: Story = {
render: () => (
<VStack spacing="l">
<Box textAlign="center">
<Heading
id="ExtraExtraExtraSmall"
level="h4"
size="heading6"
text="xxxsmall"
/>
<Image
alt="Alt text"
caption="32px"
size="xxxsmall"
src="//placekitten.com/400/300"
/>
</Box>
<Box textAlign="center">
<Heading
id="ExtraExtraSmall"
level="h4"
size="heading6"
text="xxsmall"
/>
<Image
alt="Alt text"
caption="64px"
size="xxsmall"
src="//placekitten.com/400/300"
/>
</Box>
<Box textAlign="center">
<Heading id="ExtraSmall" level="h4" size="heading6" text="xsmall" />
<Image
alt="Alt text"
caption="96px"
size="xsmall"
src="//placekitten.com/400/300"
/>
</Box>
<Box textAlign="center">
<Heading id="Small" level="h4" size="heading6" text="small" />
<Image
alt="Alt text"
caption="165px"
size="small"
src="//placekitten.com/400/300"
/>
</Box>
<Box textAlign="center">
<Heading id="Medium" level="h4" size="heading6" text="medium" />
<Image
alt="Alt text"
caption="225px"
size="medium"
src="//placekitten.com/400/300"
/>
</Box>
<Box textAlign="center">
<Heading id="Large" level="h4" size="heading6" text="large" />
<Image
alt="Alt text"
caption="360px"
size="large"
src="//placekitten.com/400/300"
/>
</Box>
<Box textAlign="center" width="100%">
<Heading id="Default" level="h4" size="heading6" text="default" />
<Image alt="Alt text" caption="100%" src="//placekitten.com/400/300" />
</Box>
</VStack>
),
render: () => allVStack(sizes),
};
export const SizesBasedOnHeight: Story = {
render: () => allVStack(sizesBasedOnHeight),
};

const imageBlockStyles = {
Expand Down
4 changes: 4 additions & 0 deletions src/components/Image/Image.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ describe("Image", () => {
const sizeLarge = renderer
.create(<Image src="test.png" alt="" size="large" />)
.toJSON();
const sizeBasedOnHeight = renderer
.create(<Image src="test.png" alt="" size="large" sizeBasedOn="height" />)
.toJSON();
const ratioFourByThree = renderer
.create(<Image src="test.png" alt="" aspectRatio="fourByThree" />)
.toJSON();
Expand Down Expand Up @@ -170,6 +173,7 @@ describe("Image", () => {
expect(sizeSmall).toMatchSnapshot();
expect(sizeMedium).toMatchSnapshot();
expect(sizeLarge).toMatchSnapshot();
expect(sizeBasedOnHeight).toMatchSnapshot();
expect(ratioFourByThree).toMatchSnapshot();
expect(ratioOneByTwo).toMatchSnapshot();
expect(ratioOriginal).toMatchSnapshot();
Expand Down
10 changes: 10 additions & 0 deletions src/components/Image/Image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ export const imageSizesArray = [
"large",
] as const;
export const imageTypesArray = ["default", "circle"] as const;
export const imageSizeBasedOnArray = ["height", "width"] as const;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can try to reuse the logoSizeBasedOnArray but rename it to something more generic for image and logo and any other component that may use height and width as values.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added dimensionTypeArray to types.ts and changed the imports.

export type ImageRatios = typeof imageRatiosArray[number];
export type ImageSizes = typeof imageSizesArray[number];
export type ImageTypes = typeof imageTypesArray[number];
export type ImageSizeBasedOn = typeof imageSizeBasedOnArray[number];

// Used for components that have an `imageProps` prop.
export interface ComponentImageProps extends Partial<HTMLImageElement> {
Expand Down Expand Up @@ -67,6 +69,8 @@ interface ImageWrapperProps {
aspectRatio?: ImageRatios;
/** Optional value to control the size of the image */
size?: ImageSizes;
/** Sets the image size based on the width or height. Width by default. */
sizeBasedOn?: ImageSizeBasedOn;
}

export interface ImageProps
Expand Down Expand Up @@ -100,11 +104,13 @@ const ImageWrapper = chakra(
children,
aspectRatio = "original",
size = "default",
sizeBasedOn = "width",
...rest
} = props;
const styles = useMultiStyleConfig("CustomImageWrapper", {
ratio: aspectRatio,
size,
sizeBasedOn,
});
return (
<Box
Expand Down Expand Up @@ -135,6 +141,7 @@ export const Image = chakra(
imageType = "default",
isLazy = false,
size = "default",
sizeBasedOn = "width",
src,
...rest
} = props;
Expand All @@ -149,7 +156,9 @@ export const Image = chakra(
const useImageWrapper = aspectRatio !== "original";
const styles = useMultiStyleConfig("CustomImage", {
variant: imageType,
ratio: aspectRatio,
size,
sizeBasedOn,
});
let imageComponent: JSX.Element | null = null;
let lazyRef = undefined;
Expand Down Expand Up @@ -196,6 +205,7 @@ export const Image = chakra(
aspectRatio={aspectRatio}
className={className}
size={size}
sizeBasedOn={sizeBasedOn}
{...(caption || credit ? {} : rest)}
>
{imageComponent}
Expand Down
Loading
Loading