From e331106e56d59a8b987230860b66214139c12ef6 Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Mon, 13 Nov 2023 18:46:31 +0100 Subject: refactor(components): rewrite ImageWidget component * remove `imageClassName` prop * replace `image` prop with `img` and expect an image instead of an object * remove `alignment prop` * remove useless CSS --- .../widgets/image-widget/image-widget.module.scss | 9 ++ .../widgets/image-widget/image-widget.stories.tsx | 115 +++++++++++++++++++++ .../widgets/image-widget/image-widget.test.tsx | 81 +++++++++++++++ .../widgets/image-widget/image-widget.tsx | 47 +++++++++ .../organisms/widgets/image-widget/index.ts | 1 + 5 files changed, 253 insertions(+) create mode 100644 src/components/organisms/widgets/image-widget/image-widget.module.scss create mode 100644 src/components/organisms/widgets/image-widget/image-widget.stories.tsx create mode 100644 src/components/organisms/widgets/image-widget/image-widget.test.tsx create mode 100644 src/components/organisms/widgets/image-widget/image-widget.tsx create mode 100644 src/components/organisms/widgets/image-widget/index.ts (limited to 'src/components/organisms/widgets/image-widget') diff --git a/src/components/organisms/widgets/image-widget/image-widget.module.scss b/src/components/organisms/widgets/image-widget/image-widget.module.scss new file mode 100644 index 0000000..b3f18ec --- /dev/null +++ b/src/components/organisms/widgets/image-widget/image-widget.module.scss @@ -0,0 +1,9 @@ +.link { + display: block; + transition: all 0.1s ease-in-out 0s; + + &:focus { + outline: 3px ridge var(--color-primary); + outline-offset: 2px; + } +} diff --git a/src/components/organisms/widgets/image-widget/image-widget.stories.tsx b/src/components/organisms/widgets/image-widget/image-widget.stories.tsx new file mode 100644 index 0000000..33f3e7b --- /dev/null +++ b/src/components/organisms/widgets/image-widget/image-widget.stories.tsx @@ -0,0 +1,115 @@ +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import NextImage from 'next/image'; +import { Heading } from '../../../atoms'; +import { ImageWidget } from './image-widget'; + +/** + * ImageWidget - Storybook Meta + */ +export default { + title: 'Organisms/Widgets/Image', + component: ImageWidget, + argTypes: { + description: { + control: { + type: 'text', + }, + description: 'Add a caption image.', + table: { + category: 'Options', + }, + type: { + name: 'string', + required: false, + }, + }, + img: { + description: 'The image.', + type: { + name: 'object', + required: true, + value: {}, + }, + }, + url: { + control: { + type: 'text', + }, + description: 'Add a link to the image.', + table: { + category: 'Options', + }, + type: { + name: 'string', + required: false, + }, + }, + }, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ( + +); + +const image = { + alt: '', + height: 480, + src: 'https://picsum.photos/640/480', + width: 640, +}; + +/** + * ImageWidget Stories - Default + */ +export const Default = Template.bind({}); +Default.args = { + heading: ( + + Quo et totam + + ), + img: , +}; + +/** + * ImageWidget Stories - WithDescription + */ +export const WithDescription = Template.bind({}); +WithDescription.args = { + description: 'Any image used as an example', + heading: ( + + Quo et totam + + ), + img: , +}; + +/** + * ImageWidget Stories - WithLink + */ +export const WithLink = Template.bind({}); +WithLink.args = { + heading: ( + + Quo et totam + + ), + img: , + url: 'https://www.armandphilippot.com/', +}; + +/** + * ImageWidget Stories - WithDescriptionAndLink + */ +export const WithDescriptionAndLink = Template.bind({}); +WithDescriptionAndLink.args = { + description: 'Any image used as an example', + heading: ( + + Quo et totam + + ), + img: , + url: 'https://www.armandphilippot.com/', +}; diff --git a/src/components/organisms/widgets/image-widget/image-widget.test.tsx b/src/components/organisms/widgets/image-widget/image-widget.test.tsx new file mode 100644 index 0000000..ecf8d77 --- /dev/null +++ b/src/components/organisms/widgets/image-widget/image-widget.test.tsx @@ -0,0 +1,81 @@ +import { describe, expect, it } from '@jest/globals'; +import { render, screen as rtlScreen } from '@testing-library/react'; +import NextImage from 'next/image'; +import { Heading } from '../../../atoms'; +import { ImageWidget } from './image-widget'; + +describe('ImageWidget', () => { + it('render the widget heading and an image', () => { + const heading = 'quam tempore ea'; + const headingLvl = 3; + const altTxt = 'enim'; + + render( + {heading}} + img={ + + } + /> + ); + + expect( + rtlScreen.getByRole('heading', { level: headingLvl }) + ).toHaveTextContent(heading); + expect(rtlScreen.getByRole('img')).toHaveAccessibleName(altTxt); + }); + + it('can render an image wrapped in a link', () => { + const heading = 'quam tempore ea'; + const headingLvl = 3; + const altTxt = 'enim'; + const url = 'https://example.test'; + + render( + {heading}} + img={ + + } + url={url} + /> + ); + + expect(rtlScreen.getByRole('link')).toHaveAttribute('href', url); + expect(rtlScreen.getByRole('img')).toHaveAccessibleName(altTxt); + }); + + it('can render an image with a description', () => { + const heading = 'quam tempore ea'; + const headingLvl = 3; + const altTxt = 'enim'; + const desc = 'itaque laudantium ut'; + + render( + {heading}} + img={ + + } + /> + ); + + expect(rtlScreen.getByRole('figure')).toHaveAccessibleName(desc); + }); +}); diff --git a/src/components/organisms/widgets/image-widget/image-widget.tsx b/src/components/organisms/widgets/image-widget/image-widget.tsx new file mode 100644 index 0000000..23e6775 --- /dev/null +++ b/src/components/organisms/widgets/image-widget/image-widget.tsx @@ -0,0 +1,47 @@ +import { + forwardRef, + type ForwardRefRenderFunction, + type ReactNode, +} from 'react'; +import { Figure, Link, type FigureProps } from '../../../atoms'; +import { Collapsible, type CollapsibleProps } from '../../../molecules'; +import styles from './image-widget.module.scss'; + +export type ImageWidgetProps = Omit & { + /** + * Add a caption to the image. + */ + description?: FigureProps['caption']; + /** + * The image. + */ + img: ReactNode; + /** + * Add a link to the image. + */ + url?: string; +}; + +const ImageWidgetWithRef: ForwardRefRenderFunction< + HTMLDivElement, + ImageWidgetProps +> = ({ description, img, isCollapsed, url, ...props }, ref) => ( + +
+ {url ? ( + + {img} + + ) : ( + img + )} +
+
+); + +/** + * ImageWidget component + * + * Renders a widget that print an image and an optional text. + */ +export const ImageWidget = forwardRef(ImageWidgetWithRef); diff --git a/src/components/organisms/widgets/image-widget/index.ts b/src/components/organisms/widgets/image-widget/index.ts new file mode 100644 index 0000000..c271b68 --- /dev/null +++ b/src/components/organisms/widgets/image-widget/index.ts @@ -0,0 +1 @@ +export * from './image-widget'; -- cgit v1.2.3