-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: Adds intersection (ViewportObserver) component (#2498)
## What's the purpose of this pull request? This PR introduces the `ViewportObserver` component, which will be used in future tasks as part of our performance improvement initiative. ## How it works? We'll primarily use it on mobile devices to prevent sections outside the viewport from being rendered initially. It checks whether a component or section is within the viewport, based on a specified height. We're using Moto G Power devices as the reference for this. ## How to test it? To facilitate the test for our future tasks, I have added a `debug` mode prop, so we can enable it for testing. In this case, we can identify if the component is IN or OUT the viewport. **This is only for the initial render.** 1. go to `core/src/components/product/ProductGrid/ProductGrid.tsx`, add `debug` prop to following component: ``` <ViewportObserver name="UIProductGrid-out-viewport" debug> ``` 2. navigate to core, run `yarn dev` 3. go to the PLP, or http://localhost:3000/office?page=0 4. set the browser device to Moto G Power <img width="500" alt="image" src="https://github.com/user-attachments/assets/b66c48fe-35de-4062-9265-6ca7801f935e"> 5. checking via console.log when the component is IN or OUT - Only the first row of the ProductGrid is inside the viewport, so you should see `OUT` in the console.log. Scrolling up the next row should appear, and you will be able to see `IN` <img width="500" alt="image" src="https://github.com/user-attachments/assets/e780ee08-dcaa-4899-8721-4eb12d9d8e7d"> 6. checking visually when the component is IN or OUT - Go to `Network` tab, selects `Slow 4G` as preset, refresh the page - You will able to see a quick visual feedback (pink rectangle and blue border) in the first ProductCard of the second row. https://github.com/user-attachments/assets/87f902b6-2e5a-47bd-8b21-3b7897de9bdd 7. checking if the first two ProductCard image is loading=`eager` and the rest `loading=`lazy` <img width="500" alt="loading-images" src="https://github.com/user-attachments/assets/76d72d87-757c-48b9-9634-c4394a93858b"> ### Starters Deploy Preview ## References https://vtex-dev.atlassian.net/browse/SFS-1507 #2404 --------- Co-authored-by: Larícia Mota <laricia.mota@vtex.com.br>
- Loading branch information
1 parent
397a6ee
commit 3885116
Showing
4 changed files
with
184 additions
and
26 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,88 @@ | ||
import type { PropsWithChildren } from 'react' | ||
import { useCallback, useEffect, useRef, useState } from 'react' | ||
|
||
// Mobile height to prevent sections outside the viewport from being rendered initially. | ||
// We are using the Moto G Power device measurement as a reference, as used by PageSpeed Insights. | ||
const VIEWPORT_SIZE = 823 | ||
|
||
type ViewportObserverProps = { | ||
/** | ||
* Identify the store section | ||
*/ | ||
sectionName?: string | ||
/** | ||
* Debug/test purposes: enables visual debugging to identify the visibility of the section. | ||
*/ | ||
debug?: boolean | ||
} & IntersectionObserverInit | ||
|
||
function ViewportObserver({ | ||
sectionName = '', | ||
threshold = 0, | ||
root = null, | ||
rootMargin, | ||
children, | ||
debug = false, | ||
}: PropsWithChildren<ViewportObserverProps>) { | ||
const [isVisible, setVisible] = useState(false) | ||
const ref = useRef<HTMLDivElement | null>(null) | ||
|
||
const observerCallback = useCallback( | ||
([entry]: IntersectionObserverEntry[], obs: IntersectionObserver) => { | ||
if (entry.isIntersecting) { | ||
if (debug) { | ||
console.log(`section '${sectionName}' VISIBLE`) | ||
document.body.style.border = '2px solid green' | ||
} | ||
setVisible(true) | ||
if (ref.current) { | ||
obs.unobserve(ref.current) | ||
} | ||
} else { | ||
setVisible(false) | ||
if (debug) { | ||
console.log(`section '${sectionName}' NOT VISIBLE`) | ||
document.body.style.border = '2px solid red' | ||
document.body.style.height = `${VIEWPORT_SIZE}px` | ||
document.body.style.boxSizing = 'border-box' | ||
} | ||
} | ||
}, | ||
[debug, sectionName] | ||
) | ||
|
||
useEffect(() => { | ||
const observer = new IntersectionObserver(observerCallback, { | ||
root, | ||
rootMargin, | ||
threshold, | ||
}) | ||
|
||
if (ref.current) { | ||
observer.observe(ref.current) | ||
} | ||
|
||
return () => observer.disconnect() | ||
}, [observerCallback, root, rootMargin, threshold]) | ||
|
||
return ( | ||
<> | ||
{!isVisible && ( | ||
<div | ||
data-store-section-name={sectionName} | ||
ref={ref} | ||
style={{ | ||
border: debug ? '2px solid red' : undefined, | ||
backgroundColor: debug ? 'red' : undefined, | ||
height: VIEWPORT_SIZE, // required to make sections out of the viewport to be rendered on demand | ||
width: '100%', | ||
}} | ||
></div> | ||
)} | ||
|
||
{isVisible && children} | ||
</> | ||
) | ||
} | ||
|
||
export default ViewportObserver |
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