Merged in feature/SW-3365-blurry-images (pull request #2746)
Feature/SW-3365 reduce upscaling of images (fix blurry images) * fix: handle when images are wider than 3:2 but rendered in a 3:2 container * use dimensions everywhere applicable * fall back to using <img sizes='auto' /> if possible * imageLoader: never nest * remove empty test file Approved-by: Anton Gunnarsson Approved-by: Matilda Landström
This commit is contained in:
@@ -43,6 +43,7 @@ export default async function MyPages({}: PageArgs<
|
|||||||
alt={hero_image.meta.alt || hero_image.meta.caption || ""}
|
alt={hero_image.meta.alt || hero_image.meta.caption || ""}
|
||||||
src={hero_image.url}
|
src={hero_image.url}
|
||||||
focalPoint={hero_image.focalPoint}
|
focalPoint={hero_image.focalPoint}
|
||||||
|
dimensions={hero_image.dimensions}
|
||||||
sizes="100vw"
|
sizes="100vw"
|
||||||
fill
|
fill
|
||||||
priority
|
priority
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export default function ContentCard({
|
|||||||
fill
|
fill
|
||||||
sizes="(min-width: 768px) 413px, 100vw"
|
sizes="(min-width: 768px) 413px, 100vw"
|
||||||
focalPoint={image.focalPoint}
|
focalPoint={image.focalPoint}
|
||||||
|
dimensions={image.dimensions}
|
||||||
/>
|
/>
|
||||||
{promoText ? (
|
{promoText ? (
|
||||||
<Chip className={styles.promoTag}>{promoText}</Chip>
|
<Chip className={styles.promoTag}>{promoText}</Chip>
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export default async function CampaignHero({
|
|||||||
fill
|
fill
|
||||||
sizes="(min-width: 768px) 800px, 100vw"
|
sizes="(min-width: 768px) 800px, 100vw"
|
||||||
focalPoint={image.focalPoint}
|
focalPoint={image.focalPoint}
|
||||||
|
dimensions={image.dimensions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export default function TopImages({ images, destinationName }: TopImageProps) {
|
|||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
focalPoint={image.focalPoint}
|
focalPoint={image.focalPoint}
|
||||||
|
dimensions={image.dimensions}
|
||||||
className={`${styles.image} ${images.length > 1 ? styles.clickable : ""}`}
|
className={`${styles.image} ${images.length > 1 ? styles.clickable : ""}`}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
images.length
|
images.length
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export default function CardImage({
|
|||||||
fill
|
fill
|
||||||
sizes="(min-width: 768px) 900px, 100vw"
|
sizes="(min-width: 768px) 900px, 100vw"
|
||||||
focalPoint={backgroundImage.focalPoint}
|
focalPoint={backgroundImage.focalPoint}
|
||||||
|
dimensions={backgroundImage.dimensions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export default function InfoCard({
|
|||||||
width={358}
|
width={358}
|
||||||
height={179}
|
height={179}
|
||||||
focalPoint={image.focalPoint}
|
focalPoint={image.focalPoint}
|
||||||
|
dimensions={image.dimensions}
|
||||||
className={styles.image}
|
className={styles.image}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export default async function StartPage({
|
|||||||
}
|
}
|
||||||
src={header.hero_image.url}
|
src={header.hero_image.url}
|
||||||
focalPoint={header.hero_image.focalPoint}
|
focalPoint={header.hero_image.focalPoint}
|
||||||
|
dimensions={header.hero_image.dimensions}
|
||||||
sizes="100vw"
|
sizes="100vw"
|
||||||
fill
|
fill
|
||||||
priority
|
priority
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export default async function StaticPage({
|
|||||||
alt={hero_image.meta.alt || hero_image.meta.caption || ""}
|
alt={hero_image.meta.alt || hero_image.meta.caption || ""}
|
||||||
src={hero_image.url}
|
src={hero_image.url}
|
||||||
focalPoint={hero_image.focalPoint}
|
focalPoint={hero_image.focalPoint}
|
||||||
|
dimensions={hero_image.dimensions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -397,6 +397,7 @@ export const renderOptions: RenderOptions = {
|
|||||||
src={image.url}
|
src={image.url}
|
||||||
width={width}
|
width={width}
|
||||||
focalPoint={image.focalPoint}
|
focalPoint={image.focalPoint}
|
||||||
|
dimensions={image.dimensions}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
<Caption>{image.meta.caption}</Caption>
|
<Caption>{image.meta.caption}</Caption>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import type { FocalPoint } from "@scandic-hotels/trpc/types/image"
|
import type { FocalPoint, Image } from "@scandic-hotels/trpc/types/image"
|
||||||
|
|
||||||
export interface HeroProps {
|
export interface HeroProps {
|
||||||
alt: string
|
alt: string
|
||||||
src: string
|
src: string
|
||||||
focalPoint?: FocalPoint
|
focalPoint?: FocalPoint
|
||||||
|
dimensions?: Image["dimension"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ import styles from "./hero.module.css"
|
|||||||
|
|
||||||
import type { HeroProps } from "./hero"
|
import type { HeroProps } from "./hero"
|
||||||
|
|
||||||
export default async function Hero({ alt, src, focalPoint }: HeroProps) {
|
export default async function Hero({
|
||||||
|
alt,
|
||||||
|
src,
|
||||||
|
focalPoint,
|
||||||
|
dimensions,
|
||||||
|
}: HeroProps) {
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
className={styles.hero}
|
className={styles.hero}
|
||||||
@@ -13,6 +18,7 @@ export default async function Hero({ alt, src, focalPoint }: HeroProps) {
|
|||||||
width={1196}
|
width={1196}
|
||||||
src={src}
|
src={src}
|
||||||
focalPoint={focalPoint}
|
focalPoint={focalPoint}
|
||||||
|
dimensions={dimensions}
|
||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ const RoomImage = memo(function RoomImage({
|
|||||||
title={roomType}
|
title={roomType}
|
||||||
fill
|
fill
|
||||||
imageCountPosition="top"
|
imageCountPosition="top"
|
||||||
sizes="(max-width: 768px) 768px, 420px"
|
|
||||||
/>
|
/>
|
||||||
<div className={styles.toggleSidePeek}>
|
<div className={styles.toggleSidePeek}>
|
||||||
{roomTypeCode && room && (
|
{roomTypeCode && room && (
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ export default function Card({
|
|||||||
fill
|
fill
|
||||||
sizes="(min-width: 1367px) 700px, 900px"
|
sizes="(min-width: 1367px) 700px, 900px"
|
||||||
focalPoint={backgroundImage.focalPoint}
|
focalPoint={backgroundImage.focalPoint}
|
||||||
|
dimensions={backgroundImage.dimensions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export default function LoyaltyCard({
|
|||||||
className={styles.image}
|
className={styles.image}
|
||||||
alt={image.meta.alt || image.title}
|
alt={image.meta.alt || image.title}
|
||||||
focalPoint={image.focalPoint}
|
focalPoint={image.focalPoint}
|
||||||
|
dimensions={image.dimensions}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<Title as="h4" level="h3" textAlign="center">
|
<Title as="h4" level="h3" textAlign="center">
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export default function TeaserCard({
|
|||||||
alt={image.meta?.alt || ""}
|
alt={image.meta?.alt || ""}
|
||||||
className={styles.image}
|
className={styles.image}
|
||||||
focalPoint={image.focalPoint}
|
focalPoint={image.focalPoint}
|
||||||
|
dimensions={image.dimensions}
|
||||||
fill
|
fill
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import NextImage, {
|
|
||||||
type ImageLoaderProps,
|
|
||||||
ImageProps as NextImageProps,
|
|
||||||
} from 'next/image'
|
|
||||||
|
|
||||||
import ImageFallback from './ImageFallback'
|
|
||||||
|
|
||||||
import type { CSSProperties } from 'react'
|
|
||||||
|
|
||||||
type FocalPoint = {
|
|
||||||
x: number
|
|
||||||
y: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ImageProps extends NextImageProps {
|
|
||||||
focalPoint?: FocalPoint
|
|
||||||
}
|
|
||||||
|
|
||||||
function imageLoader({ quality, src, width }: ImageLoaderProps) {
|
|
||||||
const isAbsoluteUrl = src.startsWith('https://') || src.startsWith('http://')
|
|
||||||
const hasQS = src.indexOf('?') !== -1
|
|
||||||
|
|
||||||
if (width < 500) {
|
|
||||||
width += 150 // HACK! Slightly increase width for better quality
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAbsoluteUrl) {
|
|
||||||
return `https://img.scandichotels.com/.netlify/images?url=${src}&w=${width}${quality ? '&q=' + quality : ''}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${src}${hasQS ? '&' : '?'}w=${width}${quality ? '&q=' + quality : ''}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next/Image adds & instead of ? before the params
|
|
||||||
export default function Image({ focalPoint, style, ...props }: ImageProps) {
|
|
||||||
const styles: CSSProperties = focalPoint
|
|
||||||
? {
|
|
||||||
objectFit: 'cover',
|
|
||||||
objectPosition: `${focalPoint.x}% ${focalPoint.y}%`,
|
|
||||||
...style,
|
|
||||||
}
|
|
||||||
: { ...style }
|
|
||||||
|
|
||||||
if (!props.src) {
|
|
||||||
return <ImageFallback />
|
|
||||||
}
|
|
||||||
|
|
||||||
return <NextImage {...props} style={styles} loader={imageLoader} />
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
import { imageLoader } from './imageLoader'
|
||||||
|
describe('imageLoader', () => {
|
||||||
|
it('should generate the correct image URL for absolute URLs', () => {
|
||||||
|
const loader = imageLoader({ dimensions: { width: 800, height: 600 } })
|
||||||
|
const url = loader({
|
||||||
|
quality: 80,
|
||||||
|
src: 'https://example.com/image.jpg',
|
||||||
|
width: 800,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(url).toBe(
|
||||||
|
'https://img.scandichotels.com/.netlify/images?url=https://example.com/image.jpg&w=800&q=80'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should generate the correct image URL for relative URLs', () => {
|
||||||
|
const loader = imageLoader({ dimensions: { width: 800, height: 600 } })
|
||||||
|
const url = loader({
|
||||||
|
quality: 80,
|
||||||
|
src: '/image.jpg',
|
||||||
|
width: 800,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(url).toBe('/image.jpg?w=800&q=80')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should compensate for landscape 3:2 images', () => {
|
||||||
|
const loader = imageLoader({ dimensions: { width: 6000, height: 4000 } })
|
||||||
|
const url = loader({
|
||||||
|
src: '/image.jpg',
|
||||||
|
width: 400,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(url).toBe('/image.jpg?w=600')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should compensate for landscape ~3:2 images', () => {
|
||||||
|
const loader = imageLoader({ dimensions: { width: 7952, height: 5304 } })
|
||||||
|
const url = loader({
|
||||||
|
src: '/image.jpg',
|
||||||
|
width: 400,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(url).toBe('/image.jpg?w=600')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should compensate for standing 2:3 images', () => {
|
||||||
|
const loader = imageLoader({ dimensions: { width: 4000, height: 6000 } })
|
||||||
|
const url = loader({
|
||||||
|
src: '/image.jpg',
|
||||||
|
width: 800,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(url).toBe('/image.jpg?w=800')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should compensate for landscape 2:1 images', () => {
|
||||||
|
const loader = imageLoader({ dimensions: { width: 2000, height: 1000 } })
|
||||||
|
const url = loader({
|
||||||
|
src: '/image.jpg',
|
||||||
|
width: 800,
|
||||||
|
})
|
||||||
|
|
||||||
|
// used to fetch an image 800x400 image but we, probably, render it with a height of 533
|
||||||
|
|
||||||
|
expect(url).toBe('/image.jpg?w=1200')
|
||||||
|
})
|
||||||
|
})
|
||||||
57
packages/design-system/lib/components/Image/imageLoader.ts
Normal file
57
packages/design-system/lib/components/Image/imageLoader.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { ImageLoaderProps } from 'next/image'
|
||||||
|
|
||||||
|
export const imageLoader =
|
||||||
|
({
|
||||||
|
dimensions,
|
||||||
|
}: {
|
||||||
|
focalPoint?: { x: number; y: number }
|
||||||
|
dimensions?: { width: number; height: number }
|
||||||
|
}) =>
|
||||||
|
({ quality, src, width }: ImageLoaderProps) => {
|
||||||
|
const isAbsoluteUrl =
|
||||||
|
src.startsWith('https://') || src.startsWith('http://')
|
||||||
|
const hasQS = src.indexOf('?') !== -1
|
||||||
|
|
||||||
|
if (
|
||||||
|
dimensions &&
|
||||||
|
isLargerThanAspectRatio(dimensions, '3:2') &&
|
||||||
|
width < dimensions.width
|
||||||
|
) {
|
||||||
|
// If image is wider than 3:2, compensate for low height when rendering in a 3:2 container
|
||||||
|
const scale = width / dimensions.width
|
||||||
|
const minWidthFor32Aspect =
|
||||||
|
dimensions.height * scale * aspectRatios['3:2'] * 2
|
||||||
|
|
||||||
|
width = Math.max(minWidthFor32Aspect, width)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width < 500) {
|
||||||
|
// Compensate for bad resizing library used by netlify
|
||||||
|
width += width / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
width = roundToNearest(width, 10)
|
||||||
|
|
||||||
|
if (isAbsoluteUrl) {
|
||||||
|
return `https://img.scandichotels.com/.netlify/images?url=${src}&w=${width}${quality ? '&q=' + quality : ''}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${src}${hasQS ? '&' : '?'}w=${width}${quality ? '&q=' + quality : ''}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const aspectRatios = {
|
||||||
|
'3:2': 3 / 2,
|
||||||
|
}
|
||||||
|
function isLargerThanAspectRatio(
|
||||||
|
dimensions: {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
},
|
||||||
|
aspectRatio: keyof typeof aspectRatios
|
||||||
|
) {
|
||||||
|
return dimensions.width / dimensions.height > aspectRatios[aspectRatio]
|
||||||
|
}
|
||||||
|
|
||||||
|
function roundToNearest(value: number, nearest: number) {
|
||||||
|
return Math.ceil(value / nearest) * nearest
|
||||||
|
}
|
||||||
46
packages/design-system/lib/components/Image/index.tsx
Normal file
46
packages/design-system/lib/components/Image/index.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import NextImage, { ImageProps as NextImageProps } from 'next/image'
|
||||||
|
|
||||||
|
import ImageFallback from '../ImageFallback'
|
||||||
|
|
||||||
|
import type { CSSProperties } from 'react'
|
||||||
|
import { imageLoader } from './imageLoader'
|
||||||
|
|
||||||
|
type FocalPoint = {
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ImageProps = NextImageProps & {
|
||||||
|
focalPoint?: FocalPoint
|
||||||
|
dimensions?: { width: number; height: number }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next/Image adds & instead of ? before the params
|
||||||
|
export default function Image({
|
||||||
|
focalPoint,
|
||||||
|
dimensions,
|
||||||
|
style,
|
||||||
|
...props
|
||||||
|
}: ImageProps) {
|
||||||
|
const styles: CSSProperties = focalPoint
|
||||||
|
? {
|
||||||
|
objectFit: 'cover',
|
||||||
|
objectPosition: `${focalPoint.x}% ${focalPoint.y}%`,
|
||||||
|
...style,
|
||||||
|
}
|
||||||
|
: { ...style }
|
||||||
|
|
||||||
|
if (!props.src) {
|
||||||
|
return <ImageFallback />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NextImage
|
||||||
|
{...props}
|
||||||
|
style={styles}
|
||||||
|
loader={imageLoader({ dimensions, focalPoint })}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -11,6 +11,10 @@ type Image = {
|
|||||||
x: number
|
x: number
|
||||||
y: number
|
y: number
|
||||||
}
|
}
|
||||||
|
dimensions?: {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
}
|
||||||
meta: {
|
meta: {
|
||||||
alt?: string | null
|
alt?: string | null
|
||||||
caption?: string | null
|
caption?: string | null
|
||||||
@@ -36,6 +40,7 @@ export default function ImageContainer({
|
|||||||
width={600}
|
width={600}
|
||||||
alt={leftImage.meta.alt || leftImage.title}
|
alt={leftImage.meta.alt || leftImage.title}
|
||||||
focalPoint={leftImage.focalPoint}
|
focalPoint={leftImage.focalPoint}
|
||||||
|
dimensions={leftImage.dimensions}
|
||||||
/>
|
/>
|
||||||
<Caption>{leftImage.meta.caption}</Caption>
|
<Caption>{leftImage.meta.caption}</Caption>
|
||||||
</article>
|
</article>
|
||||||
@@ -47,8 +52,9 @@ export default function ImageContainer({
|
|||||||
width={600}
|
width={600}
|
||||||
alt={rightImage.meta.alt || rightImage.title}
|
alt={rightImage.meta.alt || rightImage.title}
|
||||||
focalPoint={rightImage.focalPoint}
|
focalPoint={rightImage.focalPoint}
|
||||||
|
dimensions={rightImage.dimensions}
|
||||||
/>
|
/>
|
||||||
<Caption>{leftImage.meta.caption}</Caption>
|
<Caption>{rightImage.meta.caption}</Caption>
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -42,7 +42,13 @@ function ImageGallery({
|
|||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
const [imageError, setImageError] = useState(false)
|
const [imageError, setImageError] = useState(false)
|
||||||
const imageProps = fill ? { fill, sizes } : { height, width: height * 1.5 }
|
const imageProps = fill
|
||||||
|
? {
|
||||||
|
fill,
|
||||||
|
sizes:
|
||||||
|
sizes ?? 'auto, (max-width: 400px) 100vw, (min-width: 401px) 500px',
|
||||||
|
}
|
||||||
|
: { height, width: height * 1.5 }
|
||||||
|
|
||||||
if (!images || images.length === 0 || imageError) {
|
if (!images || images.length === 0 || imageError) {
|
||||||
return <ImageFallback />
|
return <ImageFallback />
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ type Image = {
|
|||||||
x: number
|
x: number
|
||||||
y: number
|
y: number
|
||||||
}
|
}
|
||||||
|
dimensions?: {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
}
|
||||||
meta: {
|
meta: {
|
||||||
alt?: string | null
|
alt?: string | null
|
||||||
caption?: string | null
|
caption?: string | null
|
||||||
|
|||||||
@@ -495,6 +495,7 @@ export const renderOptions: RenderOptions = {
|
|||||||
fill
|
fill
|
||||||
sizes="(min-width: 1367px) 800px, (max-width: 1366px) and (min-width: 1200px) 1200px, 100vw"
|
sizes="(min-width: 1367px) 800px, (max-width: 1366px) and (min-width: 1200px) 1200px, 100vw"
|
||||||
focalPoint={image.focalPoint}
|
focalPoint={image.focalPoint}
|
||||||
|
dimensions={image.dimensions}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -124,7 +124,7 @@
|
|||||||
"./Icons/WardIcon": "./lib/components/Icons/Customised/Amenities_Facilities/Ward.tsx",
|
"./Icons/WardIcon": "./lib/components/Icons/Customised/Amenities_Facilities/Ward.tsx",
|
||||||
"./Icons/WindowNotAvailableIcon": "./lib/components/Icons/Customised/Amenities_Facilities/WindowNotAvailable.tsx",
|
"./Icons/WindowNotAvailableIcon": "./lib/components/Icons/Customised/Amenities_Facilities/WindowNotAvailable.tsx",
|
||||||
"./Icons/WoodFloorIcon": "./lib/components/Icons/Customised/Amenities_Facilities/WoodFloor.tsx",
|
"./Icons/WoodFloorIcon": "./lib/components/Icons/Customised/Amenities_Facilities/WoodFloor.tsx",
|
||||||
"./Image": "./lib/components/Image.tsx",
|
"./Image": "./lib/components/Image/index.tsx",
|
||||||
"./ImageContainer": "./lib/components/ImageContainer/index.tsx",
|
"./ImageContainer": "./lib/components/ImageContainer/index.tsx",
|
||||||
"./ImageFallback": "./lib/components/ImageFallback/index.tsx",
|
"./ImageFallback": "./lib/components/ImageFallback/index.tsx",
|
||||||
"./ImageGallery": "./lib/components/ImageGallery/index.tsx",
|
"./ImageGallery": "./lib/components/ImageGallery/index.tsx",
|
||||||
|
|||||||
@@ -19,9 +19,14 @@ const browserInstances = isCI
|
|||||||
export default mergeConfig(
|
export default mergeConfig(
|
||||||
viteConfig,
|
viteConfig,
|
||||||
defineConfig({
|
defineConfig({
|
||||||
// !isCI ?
|
|
||||||
test: {
|
test: {
|
||||||
projects: [
|
projects: [
|
||||||
|
{
|
||||||
|
test: {
|
||||||
|
name: 'unit',
|
||||||
|
environment: 'jsdom',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
plugins: [
|
plugins: [
|
||||||
storybookTest({
|
storybookTest({
|
||||||
@@ -47,6 +52,5 @@ export default mergeConfig(
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
//: {}, // Netlify CI fails to run playwright tests. Only supported locally for now
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user