Change Image component lazy=true to loading=lazy (#18138)
This PR updates the `<Image>` component to follow the same property naming as native `<img>`. https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Img#attr-loading This currently allows two values,`loading=lazy` and `loading=eager`, but there might be new values added in a future spec. cc @atcastle
This commit is contained in:
parent
fe4d16c7f4
commit
07adc8ef26
5 changed files with 82 additions and 20 deletions
|
@ -1,6 +1,9 @@
|
|||
import React, { ReactElement, useEffect, useRef } from 'react'
|
||||
import Head from '../next-server/lib/head'
|
||||
|
||||
const VALID_LOADING_VALUES = ['lazy', 'eager', undefined] as const
|
||||
type LoadingValue = typeof VALID_LOADING_VALUES[number]
|
||||
|
||||
const loaders = new Map<LoaderKey, (props: LoaderProps) => string>([
|
||||
['imgix', imgixLoader],
|
||||
['cloudinary', cloudinaryLoader],
|
||||
|
@ -18,12 +21,12 @@ type ImageData = {
|
|||
|
||||
type ImageProps = Omit<
|
||||
JSX.IntrinsicElements['img'],
|
||||
'src' | 'srcSet' | 'ref' | 'width' | 'height'
|
||||
'src' | 'srcSet' | 'ref' | 'width' | 'height' | 'loading'
|
||||
> & {
|
||||
src: string
|
||||
quality?: string
|
||||
priority?: boolean
|
||||
lazy?: boolean
|
||||
loading?: LoadingValue
|
||||
unoptimized?: boolean
|
||||
} & (
|
||||
| { width: number; height: number; unsized?: false }
|
||||
|
@ -142,7 +145,7 @@ export default function Image({
|
|||
sizes,
|
||||
unoptimized = false,
|
||||
priority = false,
|
||||
lazy,
|
||||
loading,
|
||||
className,
|
||||
quality,
|
||||
width,
|
||||
|
@ -152,17 +155,23 @@ export default function Image({
|
|||
}: ImageProps) {
|
||||
const thisEl = useRef<HTMLImageElement>(null)
|
||||
|
||||
// Sanity Checks:
|
||||
// If priority and lazy are present, log an error and use priority only.
|
||||
if (priority && lazy) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!VALID_LOADING_VALUES.includes(loading)) {
|
||||
throw new Error(
|
||||
`Image with src "${src}" has both "priority" and "lazy" properties. Only one should be used.`
|
||||
`Image with src "${src}" has invalid "loading" property. Provided "${loading}" should be one of ${VALID_LOADING_VALUES.map(
|
||||
String
|
||||
).join(',')}.`
|
||||
)
|
||||
}
|
||||
if (priority && loading === 'lazy') {
|
||||
throw new Error(
|
||||
`Image with src "${src}" has both "priority" and "loading=lazy" properties. Only one should be used.`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (!priority && typeof lazy === 'undefined') {
|
||||
let lazy = loading === 'lazy'
|
||||
if (!priority && typeof loading === 'undefined') {
|
||||
lazy = true
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ const ClientSide = () => {
|
|||
<Image
|
||||
id="basic-image"
|
||||
src="foo.jpg"
|
||||
lazy={false}
|
||||
loading="eager"
|
||||
width={300}
|
||||
height={400}
|
||||
quality={60}
|
||||
|
@ -18,7 +18,7 @@ const ClientSide = () => {
|
|||
id="attribute-test"
|
||||
data-demo="demo-value"
|
||||
src="bar.jpg"
|
||||
lazy={false}
|
||||
loading="eager"
|
||||
width={300}
|
||||
height={400}
|
||||
/>
|
||||
|
@ -27,7 +27,7 @@ const ClientSide = () => {
|
|||
data-demo="demo-value"
|
||||
host="secondary"
|
||||
src="foo2.jpg"
|
||||
lazy={false}
|
||||
loading="eager"
|
||||
width={300}
|
||||
height={400}
|
||||
/>
|
||||
|
@ -35,7 +35,7 @@ const ClientSide = () => {
|
|||
id="unoptimized-image"
|
||||
unoptimized
|
||||
src="https://arbitraryurl.com/foo.jpg"
|
||||
lazy={false}
|
||||
loading="eager"
|
||||
width={300}
|
||||
height={400}
|
||||
/>
|
||||
|
|
|
@ -9,7 +9,7 @@ const Page = () => {
|
|||
<Image
|
||||
id="basic-image"
|
||||
src="foo.jpg"
|
||||
lazy={false}
|
||||
loading="eager"
|
||||
width={300}
|
||||
height={400}
|
||||
quality={60}
|
||||
|
@ -18,7 +18,7 @@ const Page = () => {
|
|||
id="attribute-test"
|
||||
data-demo="demo-value"
|
||||
src="bar.jpg"
|
||||
lazy={false}
|
||||
loading="eager"
|
||||
width={300}
|
||||
height={400}
|
||||
/>
|
||||
|
@ -27,7 +27,7 @@ const Page = () => {
|
|||
data-demo="demo-value"
|
||||
host="secondary"
|
||||
src="foo2.jpg"
|
||||
lazy={false}
|
||||
loading="eager"
|
||||
width={300}
|
||||
height={400}
|
||||
/>
|
||||
|
@ -35,7 +35,7 @@ const Page = () => {
|
|||
id="unoptimized-image"
|
||||
unoptimized
|
||||
src="https://arbitraryurl.com/foo.jpg"
|
||||
lazy={false}
|
||||
loading="eager"
|
||||
width={300}
|
||||
height={400}
|
||||
/>
|
||||
|
|
|
@ -5,12 +5,18 @@ const Lazy = () => {
|
|||
return (
|
||||
<div>
|
||||
<p id="stubtext">This is a page with lazy-loaded images</p>
|
||||
<Image id="lazy-top" src="foo1.jpg" height={400} width={300} lazy></Image>
|
||||
<Image
|
||||
id="lazy-top"
|
||||
src="foo1.jpg"
|
||||
height={400}
|
||||
width={300}
|
||||
loading="lazy"
|
||||
></Image>
|
||||
<div style={{ height: '2000px' }}></div>
|
||||
<Image
|
||||
id="lazy-mid"
|
||||
src="foo2.jpg"
|
||||
lazy
|
||||
loading="lazy"
|
||||
height={400}
|
||||
width={300}
|
||||
className="exampleclass"
|
||||
|
@ -22,7 +28,22 @@ const Lazy = () => {
|
|||
height={400}
|
||||
width={300}
|
||||
unoptimized
|
||||
lazy
|
||||
loading="lazy"
|
||||
></Image>
|
||||
<div style={{ height: '2000px' }}></div>
|
||||
<Image
|
||||
id="lazy-without-attribute"
|
||||
src="foo4.jpg"
|
||||
height={400}
|
||||
width={300}
|
||||
></Image>
|
||||
<div style={{ height: '2000px' }}></div>
|
||||
<Image
|
||||
id="eager-loading"
|
||||
src="foo5.jpg"
|
||||
loading="eager"
|
||||
height={400}
|
||||
width={300}
|
||||
></Image>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -132,6 +132,38 @@ function lazyLoadingTests() {
|
|||
await browser.elementById('lazy-bottom').getAttribute('srcset')
|
||||
).toBeFalsy()
|
||||
})
|
||||
it('should load the fourth image lazily after scrolling down', async () => {
|
||||
expect(
|
||||
await browser.elementById('lazy-without-attribute').getAttribute('src')
|
||||
).toBeFalsy()
|
||||
expect(
|
||||
await browser.elementById('lazy-without-attribute').getAttribute('srcset')
|
||||
).toBeFalsy()
|
||||
let viewportHeight = await browser.eval(`window.innerHeight`)
|
||||
let topOfBottomImage = await browser.eval(
|
||||
`document.getElementById('lazy-without-attribute').parentElement.offsetTop`
|
||||
)
|
||||
let buffer = 150
|
||||
await browser.eval(
|
||||
`window.scrollTo(0, ${topOfBottomImage - (viewportHeight + buffer)})`
|
||||
)
|
||||
await waitFor(200)
|
||||
expect(
|
||||
await browser.elementById('lazy-without-attribute').getAttribute('src')
|
||||
).toBe('https://example.com/myaccount/foo4.jpg?auto=format')
|
||||
expect(
|
||||
await browser.elementById('lazy-without-attribute').getAttribute('srcset')
|
||||
).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should load the fifth image eagerly, without scrolling', async () => {
|
||||
expect(await browser.elementById('eager-loading').getAttribute('src')).toBe(
|
||||
'https://example.com/myaccount/foo5.jpg?auto=format'
|
||||
)
|
||||
expect(
|
||||
await browser.elementById('eager-loading').getAttribute('srcset')
|
||||
).toBeTruthy()
|
||||
})
|
||||
}
|
||||
|
||||
async function hasPreloadLinkMatchingUrl(url) {
|
||||
|
|
Loading…
Reference in a new issue