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

Add initial test for observeScrollArea

Changed files
+67 -8
src
+58
src/utils/__tests__/observeScrollArea.test.tsx
··· 1 + import React, { useState, useRef, useLayoutEffect } from 'react'; 2 + import { mount } from '@cypress/react'; 3 + import { observeScrollArea } from '../observeScrollArea'; 4 + 5 + it('reports changes to the scroll area sizes', () => { 6 + const Resizeable = () => { 7 + const [minHeight, setMinHeight] = useState(50); 8 + const toggleHeight = () => setMinHeight(prev => prev === 50 ? 200 : 50); 9 + return ( 10 + <div style={{ border: '1px solid palevioletred', minHeight }}> 11 + <button id="increase" onClick={toggleHeight}>Increase Height</button> 12 + </div> 13 + ); 14 + }; 15 + 16 + const Main = () => { 17 + const [elements, setElements] = useState<number[]>([]); 18 + const [height, setHeight] = useState(-1); 19 + const ref = useRef<HTMLDivElement>(null); 20 + 21 + useLayoutEffect(() => { 22 + return observeScrollArea(ref.current!, (_width, height) => { 23 + setHeight(height); 24 + }); 25 + }); 26 + 27 + const addElement = () => setElements(prev => [...prev, prev.length]); 28 + 29 + return ( 30 + <div 31 + style={{ border: '1px solid red', overflowY: 'scroll', maxHeight: 100 }} 32 + ref={ref} 33 + > 34 + <Resizeable /> 35 + <div style={{ display: 'flex', flexDirection: 'column', minHeight: 40 }}> 36 + <span id="size">{height}</span> 37 + <button id="add" onClick={addElement}>Add Element</button> 38 + </div> 39 + {elements.map(num => ( 40 + <div style={{ height: 10, width: 10 }} key={num} /> 41 + ))} 42 + </div> 43 + ); 44 + }; 45 + 46 + mount(<Main />); 47 + 48 + cy.get('#size').contains('92'); 49 + cy.get('#increase').first().click(); 50 + cy.get('#size').contains('242'); 51 + cy.get('#increase').first().click(); 52 + cy.get('#size').contains('92'); 53 + cy.get('#increase').first().click(); 54 + cy.get('#add').first().click(); 55 + cy.get('#size').contains('252'); 56 + cy.get('#add').first().click(); 57 + cy.get('#size').contains('262'); 58 + });
+9 -8
src/utils/observeScrollArea.ts
··· 1 + const resizeOptions: ResizeObserverOptions = { box: 'border-box' }; 1 2 const mutationObservers: Map<HTMLElement, MutationObserver> = new Map(); 2 3 const resizeListeners: Map<HTMLElement, Array<() => void>> = new Map(); 3 4 ··· 40 41 for (let j = 0; j < entry.addedNodes.length; j++) { 41 42 const node = entry.addedNodes[j]; 42 43 if (node.nodeType === Node.ELEMENT_NODE) { 43 - resizeObserver.observe(node as Element); 44 + resizeObserver.observe(node as Element, resizeOptions); 44 45 } 45 46 } 46 47 ··· 53 54 } 54 55 }); 55 56 56 - const childNodes = element.childNodes; 57 - for (let i = 0; i < childNodes.length; i++) 58 - if (childNodes[i].nodeType === Node.ELEMENT_NODE) 59 - resizeObserver.observe(childNodes[i] as Element); 57 + requestAnimationFrame(() => { 58 + const childNodes = element.childNodes; 59 + for (let i = 0; i < childNodes.length; i++) 60 + if (childNodes[i].nodeType === Node.ELEMENT_NODE) 61 + resizeObserver.observe(childNodes[i] as Element, resizeOptions); 62 + mutationObserver.observe(element, { childList: true }); 63 + }); 60 64 61 - mutationObserver.observe(element, { childList: true }); 62 65 mutationObservers.set(element, mutationObserver); 63 66 } 64 - 65 - requestAnimationFrame(onResize); 66 67 67 68 return () => { 68 69 const listeners = resizeListeners.get(element) || [];