Skip to content

Commit e67b471

Browse files
authored
Merge pull request #2 from zeusdeux/update/observe-element-in-viewport
Update observe-element-in-viewport & cypress
2 parents 3b234e9 + 88c3b26 commit e67b471

File tree

5 files changed

+5694
-8113
lines changed

5 files changed

+5694
-8113
lines changed

.npmignore

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
### Node ###
2+
# Logs
3+
logs
4+
*.log
5+
npm-debug.log*
6+
yarn-debug.log*
7+
yarn-error.log*
8+
9+
# Dependency directories
10+
node_modules/
11+
12+
# Optional npm cache directory
13+
.npm
14+
15+
# Optional REPL history
16+
.node_repl_history
17+
18+
# Output of 'npm pack'
19+
*.tgz
20+
21+
# Yarn Integrity file
22+
.yarn-integrity
23+
24+
# dotenv environment variables file
25+
.env
26+
.envrc
27+
28+
# parcel-bundler cache (https://parceljs.org/)
29+
.cache
30+
31+
32+
# End of https://www.gitignore.io/api/node
33+
34+
.tern-*
35+
TAGS
36+
built/
37+
/package-lock.json
38+
cypress/screenshots
39+
cypress/videos
40+
41+
# ignore from published tarball
42+
.nowignore
43+
.prettierrc
44+
cypress.json
45+
now.json
46+
.circleci/
47+
examples/
48+
cypress/
49+
built/**/*.tsbuildinfo

Readme.md

Lines changed: 120 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ viewport.
2020
- [options.viewport](#optionsviewport)
2121
- [options.{modTop, modRight, modBottom, modLeft}](#optionsmodtop-modright-modbottom-modleft)
2222
- [Example usage](#example-usage)
23-
- [Tasks](#tasks)
2423

2524
## Motivation
2625

@@ -246,37 +245,141 @@ const [isInViewport, targetRef] = useIsInViewport({
246245
## Example usage
247246

248247
A CRA based example app (which is also used in the test suite) can be found under
249-
[examples/cra](examples/cra). Inline examples showcasing use-cases are below.
248+
[examples/cra](examples/cra). Inline examples showcasing some of the use-cases are below.
250249

251250
### Example 1: Element with its parent document as viewport
252251

253-
As soon as at least 1px of the child element is visible in the parent document viewport,
254-
`isInViewport` evaluates to true.
252+
As soon as at least 1px of the target `div` is visible in the parent document viewport,
253+
`isInViewport` evaluates to `true`.
255254

256255
```jsx
257256
import React from 'react'
258-
import ReactDOM from 'react-dom'
259257
import useIsInViewport from 'use-is-in-viewport'
260258

261-
export default function SimpleElement() {
262-
const [isInViewport, childElToWatch] = useIsInViewport()
259+
export default function MyElement() {
260+
const [isInViewport, targetRef] = useIsInViewport()
263261

264262
return (
265-
<div ref={childElToWatch}>
263+
<div ref={targetRef}>
266264
<p>{isInViewport ? 'In viewport' : 'Out of viewport'}</p>
267265
</div>
268266
)
269267
}
270268
```
271269

272-
More examples coming soon...
270+
### Example 2: Element with a custom viewport
273271

274-
## Tasks
272+
As soon as at least 1px of the target `div` is visible in its parent `div` (chosen as the parent by
273+
passing `viewportRef` to it), `isInViewport` evaluates to `true`.
275274

276-
- [x] Setup the hook to work with CRA, codesandbox and standalone react app
277-
- [x] Write the hook in a way that can be tested with Cypress
278-
- [x] Setup CI
279-
- [x] Increase test coverage
280-
- [x] Write awesome docs!
281-
- [x] Deploy example app to [useisinviewport.zdx.cat](https://useisinviewport.zdx.cat/)
282-
- [x] Document the motivation and API in the readme
275+
```jsx
276+
import React from 'react'
277+
import useIsInViewport from 'use-is-in-viewport'
278+
279+
export default function MyElement() {
280+
const [isInViewport, targetRef, viewportRef] = useIsInViewport()
281+
282+
return (
283+
<div ref={viewportRef}>
284+
<div ref={targetRef}>
285+
<p>{isInViewport ? 'In viewport' : 'Out of viewport'}</p>
286+
</div>
287+
</div>
288+
)
289+
}
290+
```
291+
292+
### Example 3: Using a forwarded ref for the target element
293+
294+
In some cases, the parent element might want to control the `ref` of some element inside your
295+
component. It is obviously up to your component to decide what element to assign the incoming `ref`.
296+
297+
In this example, we assign the incoming `ref` to the target element that we are watching for in the
298+
document viewport. To do so, we thread it through `useIsInViewport` hook by using the `target`
299+
option and assining the incoming `ref` to it. The hook takes care of updating the incoming `ref`
300+
such that it is completely transparent to the parent element that passed the `ref` in.
301+
302+
```jsx
303+
import React from 'react'
304+
import useIsInViewport from 'use-is-in-viewport'
305+
306+
export const RefForwardingElement = React.forwardRef(function RefForwardingElement(
307+
props,
308+
incomingTargetRef
309+
) {
310+
const [isInViewport, targetRef] = useIsInViewport({
311+
target: incomingTargetRef // thread the incoming target ref through the hook
312+
})
313+
314+
return (
315+
<div ref={targetRef}>
316+
<p>{isInViewport ? 'In viewport' : 'Out of viewport'}</p>
317+
</div>
318+
)
319+
})
320+
```
321+
322+
### Example 4: Using a forwarded ref for the viewport element
323+
324+
Same as the previous example, but here we thread the incoming `ref` into the viewport element
325+
instead of the target element.
326+
327+
```jsx
328+
import React from 'react'
329+
import useIsInViewport from 'use-is-in-viewport'
330+
331+
export const RefForwardingElement = React.forwardRef(function RefForwardingElement(
332+
props,
333+
incomingViewportRef
334+
) {
335+
const [isInViewport, targetRef, viewportRef] = useIsInViewport({
336+
viewport: incomingViewportRef // thread the incoming viewport ref through the hook
337+
})
338+
339+
return (
340+
<div ref={viewportRef}>
341+
<div ref={targetRef}>
342+
<p>{isInViewport ? 'In viewport' : 'Out of viewport'}</p>
343+
</div>
344+
</div>
345+
)
346+
})
347+
```
348+
349+
### Example 5: Tracking visibility in a custom viewport of multiple elements
350+
351+
In this example, we have a custom viewport element and we want to track whether its child `divs` are
352+
in viewport or not. We have also chosen to use different `threshold`s for the two child target
353+
`divs`.
354+
355+
As you might notice, we thread the `viewport` ref from the first call to `useIsInViewport` into the
356+
second call to `useIsInViewport`. This is because an element can only be assigned one `ref`. And
357+
here that element is the viewport element. Therefore, we use the transparent `ref` update capability
358+
of `useIsInViewport` (as shown in the previous examples) to wrap the viewport `ref` from the first
359+
call of the hook with a new one that is then passed to the viewport `div`.
360+
361+
```jsx
362+
import React from 'react'
363+
import useIsInViewport from 'use-is-in-viewport'
364+
365+
export default function MyElement() {
366+
const [isDivOneInViewport, divOneTargetRef, viewportRefToChain] = useIsInViewport({
367+
threshold: 50
368+
})
369+
const [isDivTwoInViewport, divTwoTargetRef, viewportRef] = useIsInViewport({
370+
viewport: viewportRefToChain,
371+
threshold: 75
372+
})
373+
374+
return (
375+
<div ref={viewportRef}>
376+
<div ref={divOneTargetRef}>
377+
<p>{isDivOneInViewport ? 'Div one In viewport' : 'Div one out of viewport'}</p>
378+
</div>
379+
<div ref={divTwoTargetRef}>
380+
<p>{isDivTwoInViewport ? 'Div two in viewport' : 'Div two out of viewport'}</p>
381+
</div>
382+
</div>
383+
)
384+
}
385+
```

0 commit comments

Comments
 (0)