Mirror: React hooks for accessible, common web interactions. UI super powers without the UI.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add initial tests for useScrollRestoration

+95 -13
+93
src/__tests__/useScrollRestoration.test.tsx
··· 1 + import React, { useState, useRef } from 'react'; 2 + import { mount } from '@cypress/react'; 3 + import { useScrollRestoration } from '../useScrollRestoration'; 4 + 5 + it('remembers scroll states and restores scrolls on navigation', () => { 6 + const Main = () => { 7 + const [showBlock, setShowBlock] = useState(false); 8 + const ref = useRef<HTMLDivElement>(null); 9 + useScrollRestoration(ref); 10 + 11 + return ( 12 + <div 13 + id="container" 14 + style={{ border: '1px solid red', overflowY: 'scroll', maxHeight: 100 }} 15 + ref={ref} 16 + > 17 + <div style={{ height: 400 }}>block #1</div> 18 + <button id="extend" onClick={() => setShowBlock(x => !x)}>Show Block</button> 19 + {showBlock && <div style={{ height: 400 }}>block #2</div>} 20 + </div> 21 + ); 22 + }; 23 + 24 + cy.window().then(window => { 25 + window.history.pushState({}, '', '' + window.location); 26 + }); 27 + 28 + mount(<Main />); 29 + 30 + cy.get('#extend').realClick(); 31 + cy.get('#container').scrollTo('bottom'); 32 + cy.get('#container').trigger('scroll'); 33 + 34 + cy.window().then(window => { 35 + window.history.pushState({}, '', '' + window.location + '?test'); 36 + }); 37 + 38 + cy.get('#container').invoke('scrollTop').should('not.equal', 0); 39 + cy.get('#container').scrollTo('top'); 40 + 41 + cy.window().then(window => { 42 + window.history.back(); 43 + }); 44 + 45 + cy.get('#container').invoke('scrollTop').should('not.equal', 0); 46 + }); 47 + 48 + it('waits to restore scroll position until required scrollHeight is reached', () => { 49 + const Main = () => { 50 + const [showBlock, setShowBlock] = useState(false); 51 + const ref = useRef<HTMLDivElement>(null); 52 + useScrollRestoration(ref); 53 + 54 + return ( 55 + <div 56 + id="container" 57 + style={{ border: '1px solid red', overflowY: 'scroll', maxHeight: 100 }} 58 + ref={ref} 59 + > 60 + <div style={{ height: 400 }}>block #1</div> 61 + <button id="extend" onClick={() => setShowBlock(x => !x)}>Show Block</button> 62 + {showBlock && <div style={{ height: 400 }}>block #2</div>} 63 + </div> 64 + ); 65 + }; 66 + 67 + cy.window().then(window => { 68 + window.history.pushState({}, '', '' + window.location); 69 + }); 70 + 71 + mount(<Main />); 72 + 73 + cy.get('#extend').realClick(); 74 + cy.get('#container').scrollTo('bottom'); 75 + cy.get('#container').trigger('scroll'); 76 + 77 + cy.window().then(window => { 78 + window.history.pushState({}, '', '' + window.location + '?test'); 79 + }); 80 + 81 + cy.get('#container').invoke('scrollTop').should('not.equal', 0); 82 + cy.get('#extend').click(); 83 + cy.get('#container').scrollTo('top'); 84 + 85 + cy.window().then(window => { 86 + window.history.back(); 87 + }); 88 + 89 + cy.get('#container').invoke('scrollTop').should('equal', 0); 90 + // once the height is reached it'll scroll back 91 + cy.get('#extend').click(); 92 + cy.get('#container').invoke('scrollTop').should('not.equal', 0); 93 + });
+2 -13
src/useScrollRestoration.ts
··· 2 2 import { observeScrollArea } from './utils/observeScrollArea'; 3 3 import { Ref } from './types'; 4 4 5 - const getIdForState = (() => { 6 - const defaultState = {}; 7 - const stateToId = new WeakMap<{}, string>(); 8 - 9 - let uniqueID = 1; 10 - 11 - return (state?: {} | null): string => { 12 - if (!state) state = defaultState; 13 - let id = stateToId.get(state); 14 - if (!id) stateToId.set(state, (id = (uniqueID++).toString(36))); 15 - return `${id}${document.location}`; 16 - }; 17 - })(); 5 + const getIdForState = (state?: {} | null): string => 6 + `${state ? JSON.stringify(state) : ''}${document.location}`; 18 7 19 8 const scrollPositions: Record<string, [number, number]> = {}; 20 9