diff options
Diffstat (limited to 'src')
5 files changed, 240 insertions, 3 deletions
diff --git a/src/components/atoms/links/link.tsx b/src/components/atoms/links/link.tsx index 0a69c33..a61158f 100644 --- a/src/components/atoms/links/link.tsx +++ b/src/components/atoms/links/link.tsx @@ -4,6 +4,10 @@ import styles from './link.module.scss'; type LinkProps = { /** + * Set additional classes to the link. + */ + classes?: string; + /** * True if it is an external link. Default: false. */ external?: boolean; @@ -22,18 +26,26 @@ type LinkProps = { * * Render a link. */ -const Link: FC<LinkProps> = ({ children, href, lang, external = false }) => { +const Link: FC<LinkProps> = ({ + children, + classes, + href, + lang, + external = false, +}) => { + const additionalClasses = classes || ''; + return external ? ( <a href={href} hrefLang={lang} - className={`${styles.link} ${styles['link--external']}`} + className={`${styles.link} ${styles['link--external']} ${additionalClasses}`} > {children} </a> ) : ( <NextLink href={href}> - <a className={styles.link}>{children}</a> + <a className={`${styles.link} ${additionalClasses}`}>{children}</a> </NextLink> ); }; 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; |
