diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 82fb2e5451..41b4dc1dca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -107,7 +107,7 @@ Package versioning and publishing is done through Lerna, by the maintainers of t ## Adding a new component -Scaffold all necessary files for a new component at once through `npm plop`. +Scaffold all necessary files for a new component at once through `npx plop`. Enter the name of your component when prompted. This will create files for the design tokens, CSS and React components, and React Stories. diff --git a/packages/css/src/image/README.md b/packages/css/src/image/README.md new file mode 100644 index 0000000000..ce7feeb1d1 --- /dev/null +++ b/packages/css/src/image/README.md @@ -0,0 +1,18 @@ +# Image + +Toont een afbeelding. + +## Richtlijnen + +- Vergeet niet om een beschrijving van de afbeelding op te nemen in het `alt`-attribuut. + Dit zorgt ervoor dat gebruikers van schermlezers deze informatie ook tot zich kunnen nemen. + Daarnaast kan het helpen bij zoekmachineoptimalisatie. +- Alleen voor decoratieve afbeeldingen is zo’n beschrijving niet nodig. Gebruik in dit geval `alt=""`. + Denk aan afbeeldingen die weinig toevoegen aan de nabije tekst of afbeeldingen die louter bijdragen aan het ontwerp of de sfeer van de pagina (bron: [W3C Web Accessibility Initiative](https://www.w3.org/WAI/tutorials/images/decorative/)). +- Zorg ervoor dat de afbeelding een beeldverhouding heeft die ondersteund wordt door het [Aspect Ratio](?path=/docs/layout-aspect-ratio--docs) component. + +## Relevante WCAG-eisen + +- [WCAG 1.1.1](https://www.w3.org/TR/WCAG22/#non-text-content): niet-tekstuele content heeft een tekstueel alternatief +- [WCAG 1.4.5](https://www.w3.org/TR/WCAG22/#images-of-text): gebruik tekst in plaats van afbeeldingen van tekst +- [WCAG 1.4.9](https://www.w3.org/TR/WCAG22/#images-of-text-no-exception): gebruik afbeeldingen van tekst alleen als er geen alternatief is diff --git a/packages/css/src/image/image.scss b/packages/css/src/image/image.scss new file mode 100644 index 0000000000..c80f4df812 --- /dev/null +++ b/packages/css/src/image/image.scss @@ -0,0 +1,16 @@ +/** + * @license EUPL-1.2+ + * Copyright (c) 2023 Gemeente Amsterdam + */ + +.amsterdam-image { + font-style: italic; /* [3] */ + height: auto; /* [1] */ + max-width: 100%; /* [1] */ + vertical-align: middle; /* [2] */ +} + +// [1] Allow for fluid image sizing while maintaining aspect ratio governed by width/height attributes +// [2] Remove ‘phantom’ whitespace +// [3] Italicise alt text to visually offset it from surrounding copy +// Source: https://x.com/csswizardry/status/1717841334462005661 diff --git a/packages/css/src/index.scss b/packages/css/src/index.scss index c1cdf7b5ae..f2a4aa7fd4 100644 --- a/packages/css/src/index.scss +++ b/packages/css/src/index.scss @@ -4,6 +4,7 @@ */ /* Append here */ +@import "./image/image"; @import "./pagination/pagination"; @import "./accordion/accordion"; @import "./alert/alert"; diff --git a/packages/react/src/Image/Image.test.tsx b/packages/react/src/Image/Image.test.tsx new file mode 100644 index 0000000000..6f07a7932f --- /dev/null +++ b/packages/react/src/Image/Image.test.tsx @@ -0,0 +1,33 @@ +import { render } from '@testing-library/react' +import { createRef } from 'react' +import { Image } from './Image' +import '@testing-library/jest-dom' + +describe('Image', () => { + it('renders', () => { + const { container } = render() + const component = container.querySelector(':only-child') + expect(component).toBeInTheDocument() + expect(component).toBeVisible() + }) + + it('renders a design system BEM class name', () => { + const { container } = render() + const component = container.querySelector(':only-child') + expect(component).toHaveClass('amsterdam-image') + }) + + it('renders an additional class name', () => { + const { container } = render() + const component = container.querySelector(':only-child') + expect(component).toHaveClass('extra') + expect(component).toHaveClass('amsterdam-image') + }) + + it('supports ForwardRef in React', () => { + const ref = createRef() + const { container } = render() + const component = container.querySelector(':only-child') + expect(ref.current).toBe(component) + }) +}) diff --git a/packages/react/src/Image/Image.tsx b/packages/react/src/Image/Image.tsx new file mode 100644 index 0000000000..6e0f6a1f19 --- /dev/null +++ b/packages/react/src/Image/Image.tsx @@ -0,0 +1,15 @@ +/** + * @license EUPL-1.2+ + * Copyright (c) 2023 Gemeente Amsterdam + */ + +import clsx from 'clsx' +import { ForwardedRef, forwardRef, ImgHTMLAttributes } from 'react' + +export interface ImageProps extends ImgHTMLAttributes {} + +export const Image = forwardRef(({ className, ...restProps }: ImageProps, ref: ForwardedRef) => ( + +)) + +Image.displayName = 'Image' diff --git a/packages/react/src/Image/README.md b/packages/react/src/Image/README.md new file mode 100644 index 0000000000..f64b03820c --- /dev/null +++ b/packages/react/src/Image/README.md @@ -0,0 +1,3 @@ +# React Image component + +[Image documentation](../../../css/src/image/README.md) diff --git a/packages/react/src/Image/index.ts b/packages/react/src/Image/index.ts new file mode 100644 index 0000000000..f9de85a6b9 --- /dev/null +++ b/packages/react/src/Image/index.ts @@ -0,0 +1,2 @@ +export { Image } from './Image' +export type { ImageProps } from './Image' diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 87b49ab25c..9147a66b5d 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -4,6 +4,7 @@ */ /* Append here */ +export * from './Image' export * from './Pagination' export * from './Screen' export * from './Switch' diff --git a/storybook/storybook-react/src/AspectRatio/AspectRatio.stories.tsx b/storybook/storybook-react/src/AspectRatio/AspectRatio.stories.tsx index e125a1a321..54be316010 100644 --- a/storybook/storybook-react/src/AspectRatio/AspectRatio.stories.tsx +++ b/storybook/storybook-react/src/AspectRatio/AspectRatio.stories.tsx @@ -3,7 +3,7 @@ * Copyright (c) 2023 Gemeente Amsterdam */ -import { AspectRatio } from '@amsterdam/design-system-react' +import { AspectRatio, Image } from '@amsterdam/design-system-react' import { Meta, StoryObj } from '@storybook/react' const meta = { @@ -57,7 +57,7 @@ const StoryTemplate: Story = { ], render: ({ ratio }) => ( - + ), } diff --git a/storybook/storybook-react/src/Card/Card.stories.tsx b/storybook/storybook-react/src/Card/Card.stories.tsx index 57fc9695a3..744c26100f 100644 --- a/storybook/storybook-react/src/Card/Card.stories.tsx +++ b/storybook/storybook-react/src/Card/Card.stories.tsx @@ -3,7 +3,7 @@ * Copyright (c) 2023 Gemeente Amsterdam */ -import { AspectRatio, Card, Heading, Paragraph } from '@amsterdam/design-system-react' +import { AspectRatio, Card, Heading, Image, Paragraph } from '@amsterdam/design-system-react' import { Meta, StoryObj } from '@storybook/react' const meta = { @@ -57,7 +57,7 @@ export const ImageCard: Story = { args: { children: [ - + , diff --git a/storybook/storybook-react/src/Grid/Grid.stories.tsx b/storybook/storybook-react/src/Grid/Grid.stories.tsx index 553b7f8006..c0c33af605 100644 --- a/storybook/storybook-react/src/Grid/Grid.stories.tsx +++ b/storybook/storybook-react/src/Grid/Grid.stories.tsx @@ -3,8 +3,7 @@ * Copyright (c) 2023 Gemeente Amsterdam */ -import { Screen } from '@amsterdam/design-system-react' -import { Grid } from '@amsterdam/design-system-react' +import { Grid, Image, Screen } from '@amsterdam/design-system-react' import { Meta, StoryObj } from '@storybook/react' const meta = { @@ -40,7 +39,7 @@ export const Cells: Story = { children: Array.from(Array(3).keys()).map((i) => (
- +
)), diff --git a/storybook/storybook-react/src/Image/Image.docs.mdx b/storybook/storybook-react/src/Image/Image.docs.mdx new file mode 100644 index 0000000000..359093682a --- /dev/null +++ b/storybook/storybook-react/src/Image/Image.docs.mdx @@ -0,0 +1,11 @@ +import { Controls, Markdown, Meta, Primary } from "@storybook/blocks"; +import * as ImageStories from "./Image.stories.tsx"; +import README from "../../../../packages/css/src/image/README.md?raw"; + + + +{README} + + + + diff --git a/storybook/storybook-react/src/Image/Image.stories.tsx b/storybook/storybook-react/src/Image/Image.stories.tsx new file mode 100644 index 0000000000..a2fcc2ac90 --- /dev/null +++ b/storybook/storybook-react/src/Image/Image.stories.tsx @@ -0,0 +1,23 @@ +/** + * @license EUPL-1.2+ + * Copyright (c) 2023 Gemeente Amsterdam + */ + +import { Image } from '@amsterdam/design-system-react' +import { Meta, StoryObj } from '@storybook/react' + +const meta = { + title: 'Media/Image', + component: Image, +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Default: Story = { + args: { + alt: '', + src: 'https://picsum.photos/1600/900', + }, +}