Created
April 29, 2022 04:47
-
-
Save kpunith8/a17a86cfc13e4081ab81dc59515d4536 to your computer and use it in GitHub Desktop.
Custom scroll hooks - refer react-use library - unit tests with RTL
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
import {useEffect} from 'react' | |
import globalProxy from '../../lib/global-proxy' | |
import useRafState from './use-raf-state' | |
// Adopted from https://github.com/streamich/react-use/blob/master/src/useWindowScroll.ts | |
export const useWindowScroll = () => { | |
const [state, setState] = useRafState(() => ({ | |
x: globalProxy().window.pageXOffset, | |
y: globalProxy().window.pageYOffset, | |
})) | |
useEffect(() => { | |
const scrollHandler = () => { | |
setState(prevState => { | |
const {pageXOffset, pageYOffset} = globalProxy().window | |
// Check state for change, return same state if no change happened to prevent rerender | |
// (see useState/setState documentation). useState/setState is used internally in useRafState/setState. | |
return prevState.x !== pageXOffset || prevState.y !== pageYOffset | |
? { | |
x: pageXOffset, | |
y: pageYOffset, | |
} | |
: prevState | |
}) | |
} | |
// We have to update window scroll at mount, before subscription. | |
// Window scroll may be changed between render and effect handler. | |
scrollHandler() | |
globalProxy().window.addEventListener('scroll', scrollHandler, { | |
capture: false, | |
passive: true, | |
}) | |
return () => { | |
globalProxy().window.removeEventListener('scroll', scrollHandler) | |
} | |
}, [setState]) | |
return state | |
} | |
// Adopted from https://github.com/streamich/react-use/blob/master/src/useScroll.ts | |
export const useScroll = ref => { | |
const [state, setState] = useRafState({x: 0, y: 0}) | |
useEffect(() => { | |
const scrollRef = ref.current | |
const scrollHandler = () => { | |
if (scrollRef) { | |
setState({x: scrollRef.scrollLeft, y: scrollRef.scrollTop}) | |
} | |
} | |
if (scrollRef) { | |
scrollRef.addEventListener('scroll', scrollHandler, { | |
capture: false, | |
passive: true, | |
}) | |
} | |
return () => { | |
if (scrollRef) { | |
scrollRef.removeEventListener('scroll', scrollHandler) | |
} | |
} | |
}, [ref, setState]) | |
return state | |
} |
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
import {useRef} from 'react' | |
import {useScroll, useWindowScroll} from './scroll-hooks' | |
import * as useRafState from './use-raf-state' | |
describe(__filename, () => { | |
describe('useScroll', () => { | |
let sandbox | |
let find | |
let yOffset = 0 | |
const DemoContainer = () => { | |
const containerRef = useRef(null) | |
const {y} = useScroll(containerRef) | |
return ( | |
<div | |
className="demo-container" | |
ref={containerRef} | |
css={{width: '100%', height: 400}} | |
> | |
<div | |
className="scroll-container" | |
css={{height: 300, overflowY: 'scroll'}} | |
> | |
Test content | |
</div> | |
<button className="submit-button" type="button" disabled={y < 100}> | |
Submit | |
</button> | |
</div> | |
) | |
} | |
beforeEach(() => { | |
sandbox = sinon.createSandbox() | |
sandbox | |
.stub(useRafState, 'default') | |
.value(() => [{x: 0, y: yOffset}, sinon.spy()]) | |
;({find} = render(<DemoContainer />)) | |
}) | |
afterEach(() => { | |
sandbox.restore() | |
}) | |
it('should disable the submit button', () => { | |
expect(find('.submit-button')).to.be.disabled() | |
}) | |
context('when the yOffset updated', () => { | |
before(() => (yOffset = 120)) | |
after(() => (yOffset = 0)) | |
it('should enable the submit button', () => { | |
expect(find('.submit-button')).not.to.be.disabled() | |
}) | |
}) | |
}) | |
describe('useWindowScroll', () => { | |
let sandbox | |
let find | |
let yOffset = 0 | |
const DemoContainer = () => { | |
const {y} = useWindowScroll() | |
return <div className={y > 0 && 'sticky')} /> | |
} | |
beforeEach(() => { | |
sandbox = sinon.createSandbox() | |
sandbox | |
.stub(useRafState, 'default') | |
.value(() => [{x: 0, y: yOffset}, sinon.spy()]) | |
;({find} = render(<DemoContainer />)) | |
}) | |
afterEach(() => { | |
sandbox.restore() | |
}) | |
it('should not have a sticky class', () => { | |
expect(find('.sticky')).not.to.be.ok | |
}) | |
context('when the yOffset updated', () => { | |
before(() => (yOffset = 10)) | |
after(() => (yOffset = 0)) | |
it('should have a sticky class', () => { | |
expect(find('.sticky')).to.be.ok | |
}) | |
}) | |
}) | |
}) |
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
import {useEffect, useRef, useState, useCallback} from 'react' | |
// Adopted from https://github.com/streamich/react-use/blob/master/src/useRafState.ts | |
const useRafState = initialState => { | |
const frame = useRef(0) | |
const [state, setState] = useState(initialState) | |
const setRafState = useCallback(value => { | |
global.cancelAnimationFrame(frame.current) | |
frame.current = global.requestAnimationFrame(() => { | |
setState(value) | |
}) | |
}, []) | |
useEffect(() => () => global.cancelAnimationFrame(frame.current), []) | |
return [state, setRafState] | |
} | |
export default useRafState |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment