From d4b29db8c725331581185d3198d6a98ce291d203 Mon Sep 17 00:00:00 2001 From: 11koukou <97431276+11koukou@users.noreply.github.com> Date: Mon, 7 Feb 2022 17:46:16 +0200 Subject: [PATCH] Fix `lazyRoot` functionality for `next/image` (#33933) Fixed lazyRoot functionality (#33290). Changed the unique id for Intersection Observers discrimination since previously they were only identified by the different rootMargins, now each being identified by the rootMargin and the root element as well Added more Images to the test with different margins and with/without lazyRoot prop. Browser correctly initially loading two of the four Images according to the props' specifications. Co-authored-by: Steven --- packages/next/client/use-intersection.tsx | 33 +++++++++-- .../default/pages/lazy-noref.js | 32 ---------- .../default/pages/lazy-withref.js | 32 +++++++++- .../default/test/index.test.js | 58 ++++++++++++++++++- 4 files changed, 114 insertions(+), 41 deletions(-) delete mode 100644 test/integration/image-component/default/pages/lazy-noref.js diff --git a/packages/next/client/use-intersection.tsx b/packages/next/client/use-intersection.tsx index 94ee9d97f1d1e8d..1b3a39db57d9d3e 100644 --- a/packages/next/client/use-intersection.tsx +++ b/packages/next/client/use-intersection.tsx @@ -13,8 +13,12 @@ type UseIntersection = { disabled?: boolean } & UseIntersectionObserverInit & { rootRef?: React.RefObject | null } type ObserveCallback = (isVisible: boolean) => void +type Identifier = { + root: Element | Document | null + margin: string +} type Observer = { - id: string + id: Identifier observer: IntersectionObserver elements: Map } @@ -83,14 +87,35 @@ function observe( if (elements.size === 0) { observer.disconnect() observers.delete(id) + let index = idList.findIndex( + (obj) => obj.root === id.root && obj.margin === id.margin + ) + if (index > -1) { + idList.splice(index, 1) + } } } } -const observers = new Map() +const observers = new Map() + +const idList: Identifier[] = [] + function createObserver(options: UseIntersectionObserverInit): Observer { - const id = options.rootMargin || '' - let instance = observers.get(id) + const id = { + root: options.root || null, + margin: options.rootMargin || '', + } + let existing = idList.find( + (obj) => obj.root === id.root && obj.margin === id.margin + ) + let instance + if (existing) { + instance = observers.get(existing) + } else { + instance = observers.get(id) + idList.push(id) + } if (instance) { return instance } diff --git a/test/integration/image-component/default/pages/lazy-noref.js b/test/integration/image-component/default/pages/lazy-noref.js deleted file mode 100644 index 3edfe7df79028d4..000000000000000 --- a/test/integration/image-component/default/pages/lazy-noref.js +++ /dev/null @@ -1,32 +0,0 @@ -import React, { useRef } from 'react' -import Image from 'next/image' - -const Page = () => { - const myRef = useRef(null) - - return ( - <> -
-
hello
-
- -
-
- - ) -} -export default Page diff --git a/test/integration/image-component/default/pages/lazy-withref.js b/test/integration/image-component/default/pages/lazy-withref.js index d21a4c0a9dad417..be990a9d4e7cda3 100644 --- a/test/integration/image-component/default/pages/lazy-withref.js +++ b/test/integration/image-component/default/pages/lazy-withref.js @@ -18,13 +18,41 @@ const Page = () => {
hello
mine + mine + mine + + mine
diff --git a/test/integration/image-component/default/test/index.test.js b/test/integration/image-component/default/test/index.test.js index 35cad0906ac75ce..8267389e1c948b2 100644 --- a/test/integration/image-component/default/test/index.test.js +++ b/test/integration/image-component/default/test/index.test.js @@ -1010,15 +1010,49 @@ function runTests(mode) { } }) - it('should load the image when the lazyRoot prop is used', async () => { + it('should initially load only two of four images using lazyroot', async () => { let browser try { - //trying on '/lazy-noref' it fails browser = await webdriver(appPort, '/lazy-withref') + await check(async () => { + const result = await browser.eval( + `document.getElementById('myImage1').naturalWidth` + ) + + if (result >= 400) { + throw new Error('Incorrectly loaded image') + } + + return 'result-correct' + }, /result-correct/) + + await check(async () => { + const result = await browser.eval( + `document.getElementById('myImage4').naturalWidth` + ) + + if (result >= 400) { + throw new Error('Incorrectly loaded image') + } + + return 'result-correct' + }, /result-correct/) await check(async () => { const result = await browser.eval( - `document.getElementById('myImage').naturalWidth` + `document.getElementById('myImage2').naturalWidth` + ) + + if (result < 400) { + throw new Error('Incorrectly loaded image') + } + + return 'result-correct' + }, /result-correct/) + + await check(async () => { + const result = await browser.eval( + `document.getElementById('myImage3').naturalWidth` ) if (result < 400) { @@ -1033,7 +1067,25 @@ function runTests(mode) { browser, `http://localhost:${appPort}/_next/image?url=%2Ftest.jpg&w=828&q=75` ) + ).toBe(false) + expect( + await hasImageMatchingUrl( + browser, + `http://localhost:${appPort}/_next/image?url=%2Ftest.png&w=828&q=75` + ) + ).toBe(true) + expect( + await hasImageMatchingUrl( + browser, + `http://localhost:${appPort}/_next/image?url=%2Ftest.svg&w=828&q=75` + ) ).toBe(true) + expect( + await hasImageMatchingUrl( + browser, + `http://localhost:${appPort}/_next/image?url=%2Ftest.webp&w=828&q=75` + ) + ).toBe(false) } finally { if (browser) { await browser.close()