diff options
Diffstat (limited to 'src/components/molecules/images')
4 files changed, 225 insertions, 0 deletions
| diff --git a/src/components/molecules/images/responsive-image.module.scss b/src/components/molecules/images/responsive-image.module.scss new file mode 100644 index 0000000..83e8d10 --- /dev/null +++ b/src/components/molecules/images/responsive-image.module.scss @@ -0,0 +1,52 @@ +@use "@styles/abstracts/functions" as fun; + +.wrapper { +  display: flex; +  flex-flow: column; +  width: 100%; +  max-width: max-content; +  margin: var(--spacing-sm) auto; +  position: relative; +  text-align: center; +} + +.caption { +  margin: 0; +  padding: fun.convert-px(4) var(--spacing-2xs); +  background: var(--color-bg-secondary); +  border: fun.convert-px(1) solid var(--color-border); +  box-shadow: 0 fun.convert-px(-1) fun.convert-px(1) fun.convert-px(1) +    var(--color-shadow-light); +  font-weight: 500; +} + +.link { +  display: flex; +  flex-flow: column; +  background: none; +  text-decoration: none; + +  .caption { +    color: var(--color-primary-darker); +  } + +  &:hover, +  &:focus { +    transform: scale(1.1); +  } + +  &:focus { +    .caption { +      text-decoration: underline solid var(--color-primary-darker) +        fun.convert-px(3); +    } +  } + +  &:active { +    transform: scale(0.9); + +    .caption { +      text-decoration: none; +    } +  } +} diff --git a/src/components/molecules/images/responsive-image.stories.tsx b/src/components/molecules/images/responsive-image.stories.tsx new file mode 100644 index 0000000..f9c1d2b --- /dev/null +++ b/src/components/molecules/images/responsive-image.stories.tsx @@ -0,0 +1,87 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import ResponsiveImageComponent from './responsive-image'; + +export default { +  title: 'Molecules/Images', +  component: ResponsiveImageComponent, +  argTypes: { +    alt: { +      control: { +        type: 'text', +      }, +      description: 'An alternative text.', +      type: { +        name: 'string', +        required: true, +      }, +    }, +    caption: { +      control: { +        type: 'text', +      }, +      description: 'A figure caption.', +      table: { +        category: 'Options', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    }, +    height: { +      control: { +        type: 'number', +      }, +      description: 'The image height.', +      type: { +        name: 'string', +        required: true, +      }, +    }, +    src: { +      control: { +        type: 'text', +      }, +      description: 'The image source.', +      type: { +        name: 'string', +        required: true, +      }, +    }, +    target: { +      control: { +        type: 'text', +      }, +      description: 'A link target.', +      table: { +        category: 'Options', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    }, +    width: { +      control: { +        type: 'number', +      }, +      description: 'The image width.', +      type: { +        name: 'string', +        required: true, +      }, +    }, +  }, +} as ComponentMeta<typeof ResponsiveImageComponent>; + +const Template: ComponentStory<typeof ResponsiveImageComponent> = (args) => ( +  <ResponsiveImageComponent {...args} /> +); + +export const ResponsiveImage = Template.bind({}); +ResponsiveImage.args = { +  alt: 'An example', +  src: 'http://placeimg.com/640/480/transport', +  width: 640, +  height: 480, +}; diff --git a/src/components/molecules/images/responsive-image.test.tsx b/src/components/molecules/images/responsive-image.test.tsx new file mode 100644 index 0000000..5452d28 --- /dev/null +++ b/src/components/molecules/images/responsive-image.test.tsx @@ -0,0 +1,18 @@ +import { render, screen } from '@test-utils'; +import ResponsiveImage from './responsive-image'; + +describe('ResponsiveImage', () => { +  it('renders a responsive image', () => { +    render( +      <ResponsiveImage +        src="http://placeimg.com/640/480" +        alt="An alternative text" +        width={640} +        height={480} +      /> +    ); +    expect( +      screen.getByRole('img', { name: 'An alternative text' }) +    ).toBeInTheDocument(); +  }); +}); diff --git a/src/components/molecules/images/responsive-image.tsx b/src/components/molecules/images/responsive-image.tsx new file mode 100644 index 0000000..db2f5ab --- /dev/null +++ b/src/components/molecules/images/responsive-image.tsx @@ -0,0 +1,68 @@ +import Link from '@components/atoms/links/link'; +import Image, { ImageProps } from 'next/image'; +import { FC } from 'react'; +import styles from './responsive-image.module.scss'; + +type ResponsiveImageProps = Omit<ImageProps, 'alt' | 'width' | 'height'> & { +  /** +   * An alternative text. +   */ +  alt: string; +  /** +   * A figure caption. +   */ +  caption?: string; +  /** +   * The image height. +   */ +  height: number | string; +  /** +   * A link target. +   */ +  target?: string; +  /** +   * The image width. +   */ +  width: number | string; +}; + +/** + * ResponsiveImage component + * + * Render a responsive image wrapped in a figure element. + */ +const ResponsiveImage: FC<ResponsiveImageProps> = ({ +  alt, +  caption, +  layout, +  objectFit, +  target, +  ...props +}) => { +  return ( +    <figure className={styles.wrapper}> +      {target ? ( +        <Link href={target} classes={styles.link}> +          <Image alt={alt} layout={layout || 'intrinsic'} {...props} /> +          {caption && ( +            <figcaption className={styles.caption}>{caption}</figcaption> +          )} +        </Link> +      ) : ( +        <> +          <Image +            alt={alt} +            layout={layout || 'intrinsic'} +            objectFit={objectFit || 'contain'} +            {...props} +          /> +          {caption && ( +            <figcaption className={styles.caption}>{caption}</figcaption> +          )} +        </> +      )} +    </figure> +  ); +}; + +export default ResponsiveImage; | 
