diff --git a/packages/components/src/ImageGallery/ImageGallery.tsx b/packages/components/src/ImageGallery/ImageGallery.tsx index ff3d2f451d..d9c3e70f8d 100644 --- a/packages/components/src/ImageGallery/ImageGallery.tsx +++ b/packages/components/src/ImageGallery/ImageGallery.tsx @@ -1,12 +1,13 @@ import { useEffect, useRef, useState } from 'react' import styled from '@emotion/styled' import PhotoSwipeLightbox from 'photoswipe/lightbox' -import { Box, Flex, Image as ThemeImage } from 'theme-ui' +import { Flex, Image as ThemeImage } from 'theme-ui' import { Icon } from '../Icon/Icon' +import { ImageGalleryThumbnail } from '../ImageGalleryThumbnail/ImageGalleryThumbnail' +import { Loader } from '../Loader/Loader' import type { PhotoSwipeOptions } from 'photoswipe/lightbox' -import type { CardProps } from 'theme-ui' import 'photoswipe/style.css' @@ -35,18 +36,9 @@ interface IState { activeImageIndex: number showLightbox: boolean images: Array + showActiveImgLoading: boolean } -const ThumbCard = styled>(Box)` - cursor: pointer; - padding: 5px; - overflow: hidden; - transition: 0.2s ease-in-out; - &:hover { - transform: translateY(-5px); - } -` - const NavButton = styled('button')` background: transparent; border: 0; @@ -62,6 +54,7 @@ export const ImageGallery = (props: ImageGalleryProps) => { activeImageIndex: 0, showLightbox: false, images: [], + showActiveImgLoading: true, }) const lightbox = useRef() @@ -113,6 +106,14 @@ export const ImageGallery = (props: ImageGalleryProps) => { setState({ ...state, activeImageIndex: imageIndex, + showActiveImgLoading: imageIndex !== activeImageIndex, + }) + } + + const setActiveImgLoaded = () => { + setState({ + ...state, + showActiveImgLoading: false, }) } @@ -132,7 +133,16 @@ export const ImageGallery = (props: ImageGalleryProps) => { return activeImage ? ( - + {state.showActiveImgLoading && ( + + )} + { height: [300, 450], }} src={activeImage.downloadUrl} - onClick={() => { - triggerLightbox() - }} + onClick={triggerLightbox} + onLoad={setActiveImgLoaded} alt={activeImage.alt ?? activeImage.name} crossOrigin="" /> @@ -193,36 +202,22 @@ export const ImageGallery = (props: ImageGalleryProps) => { ) : null} - {showThumbnails ? ( + {showThumbnails && ( {images.map((image, index: number) => ( - setActive(index)} - key={index} - > - - + ))} - ) : null} + )} ) : null } diff --git a/packages/components/src/ImageGalleryThumbnail/ImageGalleryThumbnail.stories.tsx b/packages/components/src/ImageGalleryThumbnail/ImageGalleryThumbnail.stories.tsx new file mode 100644 index 0000000000..cffed43292 --- /dev/null +++ b/packages/components/src/ImageGalleryThumbnail/ImageGalleryThumbnail.stories.tsx @@ -0,0 +1,127 @@ +import { ImageGalleryThumbnail } from './ImageGalleryThumbnail' + +import type { Meta, StoryFn } from '@storybook/react' +import type { ImageGalleryThumbnailProps } from './ImageGalleryThumbnail' + +export default { + title: 'Layout/ImageGallery/ImageGalleryThumbnail', + component: ImageGalleryThumbnail, +} as Meta + +export const Default: StoryFn = ( + props: ImageGalleryThumbnailProps, +) => { + return ( + {}} + thumbnailUrl="https://picsum.photos/id/29/150/150" + /> + ) +} + +export const AllowPortrait: StoryFn = ( + props: ImageGalleryThumbnailProps, +) => { + return ( + {}} + thumbnailUrl="https://picsum.photos/id/29/150/150" + /> + ) +} + +export const DisallowPortrait: StoryFn = ( + props: ImageGalleryThumbnailProps, +) => { + return ( + {}} + thumbnailUrl="https://picsum.photos/id/29/150/150" + /> + ) +} + +export const ImageIsActive: StoryFn = ( + props: ImageGalleryThumbnailProps, +) => { + return ( + {}} + thumbnailUrl="https://picsum.photos/id/29/150/150" + /> + ) +} + +export const ImageIsNotActive: StoryFn = ( + props: ImageGalleryThumbnailProps, +) => { + return ( + {}} + thumbnailUrl="https://picsum.photos/id/29/150/150" + /> + ) +} + +export const ThumbnailUrlInvalidAltText: StoryFn< + typeof ImageGalleryThumbnail +> = (props: ImageGalleryThumbnailProps) => { + return ( + {}} + thumbnailUrl="https://fastly.picsum.photos/404" + /> + ) +} + +export const ThumbnailUrlInvalidNameText: StoryFn< + typeof ImageGalleryThumbnail +> = (props: ImageGalleryThumbnailProps) => { + return ( + {}} + thumbnailUrl="https://fastly.picsum.photos/404" + /> + ) +} diff --git a/packages/components/src/ImageGalleryThumbnail/ImageGalleryThumbnail.test.tsx b/packages/components/src/ImageGalleryThumbnail/ImageGalleryThumbnail.test.tsx new file mode 100644 index 0000000000..99a84db991 --- /dev/null +++ b/packages/components/src/ImageGalleryThumbnail/ImageGalleryThumbnail.test.tsx @@ -0,0 +1,25 @@ +import '@testing-library/jest-dom/vitest' + +import { describe, expect, it, vi } from 'vitest' + +import { render } from '../test/utils' +import { ImageGalleryThumbnail } from './ImageGalleryThumbnail' + +describe('ImageGalleryThumbnail', () => { + it('calls Callback, when image clicked', () => { + const mockFn = vi.fn() + const { getByTestId } = render( + , + ) + getByTestId('thumbnail').click() + expect(mockFn).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/components/src/ImageGalleryThumbnail/ImageGalleryThumbnail.tsx b/packages/components/src/ImageGalleryThumbnail/ImageGalleryThumbnail.tsx new file mode 100644 index 0000000000..f8ed9f9f70 --- /dev/null +++ b/packages/components/src/ImageGalleryThumbnail/ImageGalleryThumbnail.tsx @@ -0,0 +1,65 @@ +import React, { useState } from 'react' +import styled from '@emotion/styled' +import { Box, Image as ThemeImage } from 'theme-ui' + +import { Loader } from '../Loader/Loader' + +import type { CardProps } from 'theme-ui' + +import 'photoswipe/style.css' + +export interface ImageGalleryThumbnailProps { + setActiveIndex: (index: number) => void + allowPortrait: boolean + activeImageIndex: number + thumbnailUrl: string + index: number + alt?: string + name?: string +} + +export const ThumbCard = styled>(Box)` + cursor: pointer; + padding: 5px; + overflow: hidden; + transition: 0.2s ease-in-out; + &:hover { + transform: translateY(-5px); + } +` + +export const ImageGalleryThumbnail = (props: ImageGalleryThumbnailProps) => { + const [thumbnailLoaded, setThumbnailLoaded] = useState(false) + + return ( + <> + {!thumbnailLoaded && ( + + )} + props.setActiveIndex(props.index)} + > + setThumbnailLoaded(true)} + onError={() => setThumbnailLoaded(true)} + sx={{ + width: thumbnailLoaded ? 100 : 0, + height: 67, + objectFit: props.allowPortrait ? 'contain' : 'cover', + borderRadius: 1, + border: '1px solid offWhite', + }} + crossOrigin="" + /> + + + ) +} diff --git a/packages/components/src/MapFilterList/MapFilterList.tsx b/packages/components/src/MapFilterList/MapFilterList.tsx index 0d4b354e74..5fe2e20d42 100644 --- a/packages/components/src/MapFilterList/MapFilterList.tsx +++ b/packages/components/src/MapFilterList/MapFilterList.tsx @@ -40,7 +40,9 @@ export const MapFilterList = (props: IProps) => { const isActive = (checkingFilter: string) => !!activeFilters.find((filter) => filter.label === checkingFilter) - const buttonLabel = `${pinCount} result${pinCount === 1 ? '' : 's'} in current view` + const buttonLabel = `${pinCount} result${ + pinCount === 1 ? '' : 's' + } in current view` return ( { ) return (