+58
src/utils/__tests__/observeScrollArea.test.tsx
+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
+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) || [];