mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 15:51:00 +00:00
b3ef13616f
Both side columns keep their content width, so the pill gives way: it drops its labels to icons when they no longer fit (measured, since plugins and languages change the width) and scrolls only after that. The username is no longer cut at 96px.
110 lines
3.5 KiB
TypeScript
110 lines
3.5 KiB
TypeScript
// FE-HOOK-COLLAPSE-001 to FE-HOOK-COLLAPSE-006
|
|
import { act, renderHook } from '@testing-library/react';
|
|
import { useCollapseOnOverflow } from './useCollapseOnOverflow';
|
|
|
|
function strip(scrollWidth: number, clientWidth: number): HTMLDivElement {
|
|
const el = document.createElement('div');
|
|
setWidths(el, scrollWidth, clientWidth);
|
|
return el;
|
|
}
|
|
|
|
function setWidths(el: HTMLElement, scrollWidth: number, clientWidth: number): void {
|
|
Object.defineProperty(el, 'scrollWidth', { value: scrollWidth, configurable: true });
|
|
Object.defineProperty(el, 'clientWidth', { value: clientWidth, configurable: true });
|
|
}
|
|
|
|
function resizeWindow(width: number): void {
|
|
act(() => {
|
|
window.innerWidth = width;
|
|
window.dispatchEvent(new Event('resize'));
|
|
});
|
|
}
|
|
|
|
function mount(el: HTMLDivElement, key = 'a|b|c') {
|
|
return renderHook(
|
|
({ k }) => {
|
|
const result = useCollapseOnOverflow<HTMLDivElement>(k);
|
|
result.ref.current = el;
|
|
return result;
|
|
},
|
|
{ initialProps: { k: key } }
|
|
);
|
|
}
|
|
|
|
const originalWidth = window.innerWidth;
|
|
afterEach(() => {
|
|
window.innerWidth = originalWidth;
|
|
});
|
|
|
|
describe('useCollapseOnOverflow', () => {
|
|
it('FE-HOOK-COLLAPSE-001: keeps the labels while the strip fits', () => {
|
|
window.innerWidth = 1600;
|
|
const { result } = mount(strip(500, 500));
|
|
resizeWindow(1500);
|
|
expect(result.current.collapsed).toBe(false);
|
|
});
|
|
|
|
it('FE-HOOK-COLLAPSE-002: collapses once the strip overflows', () => {
|
|
window.innerWidth = 1600;
|
|
const el = strip(500, 500);
|
|
const { result } = mount(el);
|
|
setWidths(el, 740, 640);
|
|
resizeWindow(1200);
|
|
expect(result.current.collapsed).toBe(true);
|
|
});
|
|
|
|
it('FE-HOOK-COLLAPSE-003: brings the labels back only once the window has the missing width', () => {
|
|
window.innerWidth = 1200;
|
|
const el = strip(740, 640);
|
|
const { result } = mount(el);
|
|
expect(result.current.collapsed).toBe(true);
|
|
// Collapsed, the strip fits by definition, so only the window width counts.
|
|
setWidths(el, 320, 320);
|
|
resizeWindow(1250);
|
|
expect(result.current.collapsed).toBe(true);
|
|
setWidths(el, 740, 740);
|
|
resizeWindow(1300);
|
|
expect(result.current.collapsed).toBe(false);
|
|
});
|
|
|
|
it('FE-HOOK-COLLAPSE-004: measures again when the set of items changes', () => {
|
|
window.innerWidth = 1200;
|
|
const el = strip(740, 640);
|
|
const { result, rerender } = mount(el);
|
|
expect(result.current.collapsed).toBe(true);
|
|
// One tab fewer: the labels fit at the same window width.
|
|
setWidths(el, 600, 600);
|
|
rerender({ k: 'a|b' });
|
|
expect(result.current.collapsed).toBe(false);
|
|
});
|
|
|
|
it('FE-HOOK-COLLAPSE-005: stops listening on unmount', () => {
|
|
const remove = vi.spyOn(window, 'removeEventListener');
|
|
const { unmount } = mount(strip(500, 500));
|
|
unmount();
|
|
expect(remove).toHaveBeenCalledWith('resize', expect.any(Function));
|
|
remove.mockRestore();
|
|
});
|
|
it('FE-HOOK-COLLAPSE-006: measures again when the strip itself changes size, a font or text size with it', () => {
|
|
let resized: (() => void) | null = null;
|
|
vi.stubGlobal(
|
|
'ResizeObserver',
|
|
class {
|
|
constructor(cb: () => void) {
|
|
resized = cb;
|
|
}
|
|
observe() {}
|
|
disconnect() {}
|
|
}
|
|
);
|
|
window.innerWidth = 1600;
|
|
const el = strip(500, 500);
|
|
const { result } = mount(el);
|
|
expect(result.current.collapsed).toBe(false);
|
|
setWidths(el, 700, 600);
|
|
act(() => resized?.());
|
|
expect(result.current.collapsed).toBe(true);
|
|
vi.unstubAllGlobals();
|
|
});
|
|
});
|