Skip to content

Commit

Permalink
Add initial image component (#741)
Browse files Browse the repository at this point in the history
* Correct plop command in docs

* Add initial Image component

* Use image component in other stories

* Add more applicable a11y links

* Remove properties we’re not using yet

* Remove empty file

* Improve docs

Co-authored-by: Aram <37216945+alimpens@users.noreply.github.com>

---------

Co-authored-by: Aram <37216945+alimpens@users.noreply.github.com>
  • Loading branch information
VincentSmedinga and alimpens authored Nov 13, 2023
1 parent 752514a commit 7e2e11f
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 8 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
18 changes: 18 additions & 0 deletions packages/css/src/image/README.md
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions packages/css/src/image/image.scss
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions packages/css/src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

/* Append here */
@import "./image/image";
@import "./pagination/pagination";
@import "./accordion/accordion";
@import "./alert/alert";
Expand Down
33 changes: 33 additions & 0 deletions packages/react/src/Image/Image.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<Image />)
const component = container.querySelector(':only-child')
expect(component).toBeInTheDocument()
expect(component).toBeVisible()
})

it('renders a design system BEM class name', () => {
const { container } = render(<Image />)
const component = container.querySelector(':only-child')
expect(component).toHaveClass('amsterdam-image')
})

it('renders an additional class name', () => {
const { container } = render(<Image className="extra" />)
const component = container.querySelector(':only-child')
expect(component).toHaveClass('extra')
expect(component).toHaveClass('amsterdam-image')
})

it('supports ForwardRef in React', () => {
const ref = createRef<HTMLImageElement>()
const { container } = render(<Image ref={ref} />)
const component = container.querySelector(':only-child')
expect(ref.current).toBe(component)
})
})
15 changes: 15 additions & 0 deletions packages/react/src/Image/Image.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLImageElement> {}

export const Image = forwardRef(({ className, ...restProps }: ImageProps, ref: ForwardedRef<HTMLImageElement>) => (
<img {...restProps} ref={ref} className={clsx('amsterdam-image', className)} />
))

Image.displayName = 'Image'
3 changes: 3 additions & 0 deletions packages/react/src/Image/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# React Image component

[Image documentation](../../../css/src/image/README.md)
2 changes: 2 additions & 0 deletions packages/react/src/Image/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Image } from './Image'
export type { ImageProps } from './Image'
1 change: 1 addition & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

/* Append here */
export * from './Image'
export * from './Pagination'
export * from './Screen'
export * from './Switch'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -57,7 +57,7 @@ const StoryTemplate: Story = {
],
render: ({ ratio }) => (
<AspectRatio ratio={ratio} style={{ maxWidth: ratio ? storyConfig[ratio].maxWidth : '500px' }}>
<img alt="" src={ratio ? storyConfig[ratio].image : 'https://picsum.photos/800/800'} style={{ width: '100%' }} />
<Image alt="" src={ratio ? storyConfig[ratio].image : 'https://picsum.photos/800/800'} />
</AspectRatio>
),
}
Expand Down
4 changes: 2 additions & 2 deletions storybook/storybook-react/src/Card/Card.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -57,7 +57,7 @@ export const ImageCard: Story = {
args: {
children: [
<AspectRatio key={1} ratio="wide">
<img alt="" src="https://picsum.photos/440/352" />
<Image alt="" src="https://picsum.photos/440/352" />
</AspectRatio>,
<Card.HeadingGroup key={2} tagline="Dossier">
<Heading size="level-4">
Expand Down
5 changes: 2 additions & 3 deletions storybook/storybook-react/src/Grid/Grid.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -40,7 +39,7 @@ export const Cells: Story = {
children: Array.from(Array(3).keys()).map((i) => (
<Grid.Cell key={i} span={4}>
<figure className="amsterdam-docs-figure">
<img alt="" src={`https://picsum.photos/1024/576?random=${i}`} />
<Image alt="" src={`https://picsum.photos/1024/576?random=${i}`} />
</figure>
</Grid.Cell>
)),
Expand Down
11 changes: 11 additions & 0 deletions storybook/storybook-react/src/Image/Image.docs.mdx
Original file line number Diff line number Diff line change
@@ -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";

<Meta of={ImageStories} />

<Markdown>{README}</Markdown>

<Primary />

<Controls />
23 changes: 23 additions & 0 deletions storybook/storybook-react/src/Image/Image.stories.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof Image>

export default meta

type Story = StoryObj<typeof meta>

export const Default: Story = {
args: {
alt: '',
src: 'https://picsum.photos/1600/900',
},
}

0 comments on commit 7e2e11f

Please sign in to comment.