107 lines
3.6 KiB
TypeScript
107 lines
3.6 KiB
TypeScript
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import MobileDrawer from './MobileDrawer';
|
|
|
|
vi.mock('react-i18next', () => ({
|
|
useTranslation: () => ({
|
|
t: (key: string) => key,
|
|
}),
|
|
}));
|
|
|
|
const originalSetPointerCapture = HTMLElement.prototype.setPointerCapture;
|
|
|
|
function renderDrawer(onClose = vi.fn()) {
|
|
const view = render(
|
|
<MobileDrawer
|
|
onClose={onClose}
|
|
renderArea={() => <div>Area content</div>}
|
|
renderProperties={() => <div>Properties content</div>}
|
|
tab="area"
|
|
onTabChange={vi.fn()}
|
|
/>
|
|
);
|
|
const handle = view.container.querySelector('[data-mobile-drawer-drag-handle]');
|
|
const root = view.container.querySelector('[data-tutorial="right-pane"]');
|
|
const panel = view.container.querySelector('[data-tutorial="right-pane"] > div:last-child');
|
|
|
|
if (!(handle instanceof HTMLElement)) throw new Error('Expected drawer drag handle');
|
|
if (!(root instanceof HTMLElement)) throw new Error('Expected drawer root');
|
|
if (!(panel instanceof HTMLElement)) throw new Error('Expected drawer panel');
|
|
|
|
return { ...view, handle, onClose, panel, root };
|
|
}
|
|
|
|
describe('MobileDrawer', () => {
|
|
beforeEach(() => {
|
|
HTMLElement.prototype.setPointerCapture = vi.fn();
|
|
});
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
Object.defineProperty(HTMLElement.prototype, 'setPointerCapture', {
|
|
configurable: true,
|
|
value: originalSetPointerCapture,
|
|
});
|
|
});
|
|
|
|
it('lowers and stays open when swiped down from the handle', () => {
|
|
const { handle, onClose, panel } = renderDrawer();
|
|
|
|
fireEvent.pointerDown(handle, { pointerId: 1, clientY: 120 });
|
|
fireEvent.pointerMove(handle, { pointerId: 1, clientY: 230 });
|
|
fireEvent.pointerUp(handle, { pointerId: 1, clientY: 230 });
|
|
|
|
expect(onClose).not.toHaveBeenCalled();
|
|
expect(panel.style.transform).toBe('translateY(110px)');
|
|
});
|
|
|
|
it('can be raised again after being lowered', () => {
|
|
const { handle, onClose, panel } = renderDrawer();
|
|
|
|
fireEvent.pointerDown(handle, { pointerId: 1, clientY: 120 });
|
|
fireEvent.pointerMove(handle, { pointerId: 1, clientY: 230 });
|
|
fireEvent.pointerUp(handle, { pointerId: 1, clientY: 230 });
|
|
|
|
fireEvent.pointerDown(handle, { pointerId: 2, clientY: 230 });
|
|
fireEvent.pointerMove(handle, { pointerId: 2, clientY: 170 });
|
|
fireEvent.pointerUp(handle, { pointerId: 2, clientY: 170 });
|
|
|
|
expect(onClose).not.toHaveBeenCalled();
|
|
expect(panel.style.transform).toBe('translateY(50px)');
|
|
});
|
|
|
|
it('keeps the close control reachable when dragged down far', () => {
|
|
const { handle, panel } = renderDrawer();
|
|
|
|
Object.defineProperty(panel, 'offsetHeight', {
|
|
configurable: true,
|
|
value: 200,
|
|
});
|
|
|
|
fireEvent.pointerDown(handle, { pointerId: 1, clientY: 120 });
|
|
fireEvent.pointerMove(handle, { pointerId: 1, clientY: 420 });
|
|
fireEvent.pointerUp(handle, { pointerId: 1, clientY: 420 });
|
|
|
|
expect(panel.style.transform).toBe('translateY(96px)');
|
|
});
|
|
|
|
it('leaves the rest of the mobile map usable while the panel is open', () => {
|
|
const { panel, root } = renderDrawer();
|
|
const spacer = root.firstElementChild;
|
|
|
|
if (!(spacer instanceof HTMLElement)) throw new Error('Expected drawer spacer');
|
|
|
|
expect(root.className).toContain('pointer-events-none');
|
|
expect(panel.className).toContain('pointer-events-auto');
|
|
expect(spacer.className).not.toContain('bg-black');
|
|
});
|
|
|
|
it('closes from the close button', () => {
|
|
const { onClose } = renderDrawer();
|
|
|
|
fireEvent.click(screen.getByLabelText('mobileDrawer.closeDrawer'));
|
|
|
|
expect(onClose).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|