Skip to content

Commit

Permalink
Implement hero image component
Browse files Browse the repository at this point in the history
  • Loading branch information
VincentSmedinga committed Dec 12, 2023
1 parent 9993d8d commit af364e9
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 0 deletions.
3 changes: 3 additions & 0 deletions packages/css/src/components/hero-image/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Hero Image

Een sfeerbepalende afbeelding die vaak over de volle breedte bovenaan een pagina wordt gebruikt.
13 changes: 13 additions & 0 deletions packages/css/src/components/hero-image/hero-image.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @license EUPL-1.2+
* Copyright (c) 2023 Gemeente Amsterdam
*/

.amsterdam-hero-image {
align-content: center;
aspect-ratio: var(--amsterdam-hero-image-aspect-ratio);
background-position: center;
background-repeat: no-repeat;
background-size: cover;
display: grid;
}
1 change: 1 addition & 0 deletions packages/css/src/components/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

/* Append here */
@import "./hero-image/hero-image";
@import "./text-input/text-input";
@import "./search-field/search-field";
@import "./logo/logo";
Expand Down
32 changes: 32 additions & 0 deletions packages/react/src/HeroImage/HeroImage.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { render } from '@testing-library/react'
import { createRef } from 'react'
import { HeroImage } from './HeroImage'
import '@testing-library/jest-dom'

describe('Hero image', () => {
it('renders', () => {
const { container } = render(<HeroImage src="'https://picsum.photos/1280/360'" />)
const component = container.querySelector(':only-child')
expect(component).toBeInTheDocument()
expect(component).toBeVisible()
})

it('renders a design system BEM class name', () => {
const { container } = render(<HeroImage src="'https://picsum.photos/1280/360'" />)
const component = container.querySelector(':only-child')
expect(component).toHaveClass('amsterdam-hero-image')
})

it('renders an additional class name', () => {
const { container } = render(<HeroImage src="'https://picsum.photos/1280/360'" className="extra" />)
const component = container.querySelector(':only-child')
expect(component).toHaveClass('amsterdam-hero-image extra')
})

it('supports ForwardRef in React', () => {
const ref = createRef<HTMLDivElement>()
const { container } = render(<HeroImage src="'https://picsum.photos/1280/360'" ref={ref} />)
const component = container.querySelector(':only-child')
expect(ref.current).toBe(component)
})
})
32 changes: 32 additions & 0 deletions packages/react/src/HeroImage/HeroImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* @license EUPL-1.2+
* Copyright (c) 2023 Gemeente Amsterdam
*/

import clsx from 'clsx'
import { ImgHTMLAttributes } from 'react'
import { forwardRef, HTMLAttributes, PropsWithChildren } from 'react'

export interface HeroImageProps extends PropsWithChildren<HTMLAttributes<HTMLElement>> {
as?: 'article' | 'aside' | 'div' | 'footer' | 'section'
src: ImgHTMLAttributes<HTMLImageElement>['src']
}

export const HeroImage = forwardRef<HTMLDivElement, HeroImageProps>(
({ as = 'div', children, className, src, ...restProps }: HeroImageProps, ref) => {
const Component = as

return (
<Component
{...restProps}
ref={ref}
className={clsx('amsterdam-hero-image', className)}
style={{ backgroundImage: `url(${src ?? 'https://picsum.photos/1280/360'})` }}
>
{children}
</Component>
)
},
)

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

[Hero-Image documentation](../../../css/src/hero-image/README.md)
2 changes: 2 additions & 0 deletions packages/react/src/HeroImage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { HeroImage } from './HeroImage'
export type { HeroImageProps } from './HeroImage'
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 './HeroImage'
export * from './TextInput'
export * from './SearchField'
export * from './Logo'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"amsterdam": {
"hero-image": {
"aspect-ratio": { "value": "{amsterdam.proportion.2x-wide}" }
}
}
}
25 changes: 25 additions & 0 deletions storybook/storybook-react/src/HeroImage/HeroImage.docs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Canvas, Markdown, Meta, Primary } from "@storybook/blocks";
import * as HeroImageStories from "./HeroImage.stories.tsx";
import README from "../../../../packages/css/src/components/hero-image/README.md?raw";

<Meta of={HeroImageStories} />

<Markdown>{README}</Markdown>

## Voorbeelden

### Basis

De beeldverhouding van de afbeelding is 32:9.
Dat komt overeen met de `2x-wide` [aspect ratio](?path=/docs/react_layout-aspect-ratio--docs).

<Primary />

### Met zoekveld

De inhoud van een hero-afbeelding zal doorgaans in een grid staan.
De horizontale witruimte en maatvoering is dan verzorgd.

In dit geval specificeert het zoekveld een maximale breedte en centreert het zichzelf binnen de cel van het grid.

<Canvas of={HeroImageStories.WithSearchField} />
43 changes: 43 additions & 0 deletions storybook/storybook-react/src/HeroImage/HeroImage.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* @license EUPL-1.2+
* Copyright (c) 2023 Gemeente Amsterdam
*/

import { Grid } from '@amsterdam/design-system-react'
import { SearchField } from '@amsterdam/design-system-react'
import { HeroImage } from '@amsterdam/design-system-react'
import { Meta, StoryObj } from '@storybook/react'

const meta = {
title: 'Media/Hero Image',
component: HeroImage,
args: {
children: <SearchField />,
},
} satisfies Meta<typeof HeroImage>

export default meta

type Story = StoryObj<typeof meta>

export const Default: Story = {
args: {
src: 'https://picsum.photos/1280/360?random=1',
},
}

export const WithSearchField: Story = {
args: {
src: 'https://picsum.photos/1280/360?random=2',
children: (
<Grid paddingVertical="medium">
<Grid.Cell fullWidth>
<SearchField style={{ marginInline: 'auto', maxWidth: '32rem' }}>
<SearchField.Input label="Zoeken" placeholder="Wat kunnen we voor u vinden?" />
<SearchField.Button />
</SearchField>
</Grid.Cell>
</Grid>
),
},
}

0 comments on commit af364e9

Please sign in to comment.