-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Ruben Sibon <[email protected]> Co-authored-by: Vincent Smedinga <[email protected]>
- Loading branch information
1 parent
20bc950
commit 58e7766
Showing
24 changed files
with
946 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<!-- @license CC0-1.0 --> | ||
|
||
# Image Slider | ||
|
||
Displays a small set of images in a limited space. | ||
|
||
## Design | ||
|
||
The first or selected image shows at its maximum size. | ||
Every image displays a thumbnail at about 20% of its width. | ||
Users can use buttons, thumbnails or swiping to navigate between the images. | ||
The buttons re not displayed on a narrow touch device. | ||
The images do not slide automatically. | ||
|
||
## How to use | ||
|
||
- Use this for a series of images that belong together. | ||
- Feature the most essential image first. | ||
- Display the Image Slider at the entire width of the [Screen](/docs/components-layout-screen--docs); do not position it on the [Grid](/docs/components-layout-grid--docs). | ||
- Provide at least 2 images but at most 5. | ||
- Assume that some or many users will not use the Slider and only see the first image. | ||
Display all images separately if you want each of them to receive attention. | ||
- Consult the [Image](/docs/components-media-image--docs) docs for guidelines on the individual images. |
89 changes: 89 additions & 0 deletions
89
packages/css/src/components/image-slider/image-slider.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/** | ||
* @license EUPL-1.2+ | ||
* Copyright Gemeente Amsterdam | ||
*/ | ||
@import "../../common/breakpoint"; | ||
|
||
.ams-image-slider { | ||
display: grid; | ||
gap: var(--ams-image-slider-gap); | ||
grid-template-rows: 1fr auto; | ||
} | ||
|
||
.ams-image-slider__item { | ||
scroll-snap-align: center; | ||
scroll-snap-stop: always; | ||
|
||
/** temporary fix for covering the entire gallery */ | ||
.ams-image { | ||
inline-size: 100%; | ||
} | ||
} | ||
|
||
.ams-image-slider__scroller { | ||
align-items: center; | ||
display: grid; | ||
gap: var(--ams-image-slider-scroller-gap); | ||
grid-auto-columns: 100%; | ||
grid-auto-flow: column; | ||
grid-column: 1/-1; | ||
grid-row: 1; | ||
outline-offset: var(--ams-image-slider-scroller-outline-offset); | ||
overflow-x: auto; | ||
overscroll-behavior-x: contain; | ||
scroll-snap-type: x mandatory; | ||
scrollbar-width: none; | ||
|
||
&::-webkit-scrollbar { | ||
display: none; | ||
} | ||
|
||
@media not (prefers-reduced-motion) { | ||
scroll-behavior: smooth; | ||
} | ||
} | ||
|
||
.ams-image-slider__controls { | ||
display: flex; | ||
grid-column: 1/-1; | ||
grid-row: 1; | ||
justify-content: space-between; | ||
|
||
@media (pointer: coarse) and (max-width: $ams-breakpoint-medium) { | ||
display: none; | ||
} | ||
} | ||
|
||
.ams-image-slider__control { | ||
place-self: center; | ||
z-index: 1; | ||
} | ||
|
||
.ams-image-slider__thumbnails { | ||
display: grid; | ||
gap: var(--ams-image-slider-thumbnails-gap); | ||
grid-template-columns: repeat(5, 1fr); | ||
max-inline-size: 100%; | ||
} | ||
|
||
.ams-image-slider__thumbnail { | ||
background-color: var(--ams-image-slider-thumbnails-thumbnail-background-color); | ||
background-position: center; | ||
background-size: cover; | ||
border: none; | ||
cursor: var(--ams-image-slider-thumbnails-thumbnail-cursor); | ||
opacity: var(--ams-image-slider-thumbnails-thumbnail-opacity); | ||
outline-offset: var(--ams-button-outline-offset); | ||
padding-block: 0; | ||
padding-inline: 0; | ||
position: relative; | ||
scroll-snap-align: start; | ||
|
||
&:hover { | ||
opacity: var(--ams-image-slider-thumbnails-thumbnail-hover-opacity); | ||
} | ||
} | ||
|
||
.ams-image-slider__thumbnail--in-view { | ||
opacity: var(--ams-image-slider-thumbnails-thumbnail-in-view-opacity); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { render } from '@testing-library/react' | ||
import { createRef } from 'react' | ||
import { ImageSlider, ImageSliderImageProps } from './ImageSlider' | ||
import '@testing-library/jest-dom' | ||
|
||
const observe = jest.fn() | ||
const unobserve = jest.fn() | ||
const disconnect = jest.fn() | ||
const takeRecords = jest.fn() | ||
|
||
// Mock implementation of IntersectionObserver | ||
window.IntersectionObserver = jest.fn(() => ({ | ||
observe, | ||
unobserve, | ||
disconnect, | ||
takeRecords, | ||
root: null, | ||
rootMargin: '', | ||
thresholds: [], | ||
})) | ||
|
||
describe('Image Slider', () => { | ||
const images: ImageSliderImageProps[] = [ | ||
{ | ||
alt: 'Bridge', | ||
aspectRatio: 'x-wide', | ||
src: 'https://picsum.photos/id/122/320/180', | ||
}, | ||
{ | ||
alt: 'Bunker', | ||
aspectRatio: 'x-wide', | ||
src: 'https://picsum.photos/id/101/320/180', | ||
}, | ||
{ | ||
alt: 'Chairs', | ||
aspectRatio: 'x-wide', | ||
src: 'https://picsum.photos/id/153/320/180', | ||
}, | ||
] | ||
|
||
it('renders', () => { | ||
const { container } = render(<ImageSlider images={images} />) | ||
|
||
const component = container.querySelector(':only-child') | ||
|
||
expect(component).toBeInTheDocument() | ||
expect(component).toBeVisible() | ||
}) | ||
|
||
it('renders slides', () => { | ||
const { container } = render(<ImageSlider images={images} />) | ||
|
||
const slides = Array.from(container.querySelectorAll('.ams-image-slider__item')) | ||
|
||
expect(slides).toHaveLength(3) | ||
}) | ||
|
||
it('renders a design system BEM class name', () => { | ||
const { container } = render(<ImageSlider images={images} />) | ||
|
||
const component = container.querySelector(':only-child') | ||
|
||
expect(component).toHaveClass('ams-image-slider') | ||
}) | ||
|
||
it('renders an additional class name', () => { | ||
const { container } = render(<ImageSlider className="extra" images={images} />) | ||
|
||
const component = container.querySelector(':only-child') | ||
|
||
expect(component).toHaveClass('ams-image-slider extra') | ||
}) | ||
|
||
it('supports ForwardRef in React', () => { | ||
const ref = createRef<HTMLDivElement>() | ||
|
||
const { container } = render(<ImageSlider images={images} ref={ref} />) | ||
|
||
const component = container.querySelector(':only-child') | ||
|
||
expect(ref.current).toBe(component) | ||
}) | ||
|
||
it('renders thumbnails', () => { | ||
const { container } = render(<ImageSlider images={images} />) | ||
|
||
expect(container.querySelector('.ams-image-slider__thumbnails')).toBeInTheDocument() | ||
}) | ||
}) |
Oops, something went wrong.