146 lines
4.2 KiB
TypeScript
146 lines
4.2 KiB
TypeScript
import { describe, test, expect, vi } from 'vitest';
|
|||
|
|
import React from 'react';
|
||
|
|
import { createRoot, Root } from 'react-dom/client';
|
||
|
|
import { act } from 'react-dom/test-utils';
|
||
|
|
import { MobilePanelBar } from './MobilePanelBar';
|
||
|
|
import { MobileChromeProvider } from '../../state/MobileChromeContext';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Mobile-A2 (review item 5): the real BlocksPanel/PagesPanel/LayersPanel/
|
||
|
|
* AssetsPanel/GuidedStyles all pull in Craft.js's `useEditor` and other
|
||
|
|
* heavy dependencies not relevant here, so they're stubbed out -- this
|
||
|
|
* suite is only exercising MobilePanelBar's OWN wiring: tapping a tab opens
|
||
|
|
* that tab's sheet with only its panel rendered, tapping the active tab
|
||
|
|
* again closes it, and (mirroring MobileChromeContext.test.tsx) only one
|
||
|
|
* sheet's content is ever mounted at once.
|
||
|
|
*/
|
||
|
|
vi.mock('../left/BlocksPanel', () => ({ BlocksPanel: () => <div data-testid="panel-blocks" /> }));
|
||
|
|
vi.mock('../left/PagesPanel', () => ({ PagesPanel: () => <div data-testid="panel-pages" /> }));
|
||
|
|
vi.mock('../left/LayersPanel', () => ({ LayersPanel: () => <div data-testid="panel-layers" /> }));
|
||
|
|
vi.mock('../left/AssetsPanel', () => ({ AssetsPanel: () => <div data-testid="panel-assets" /> }));
|
||
|
|
vi.mock('../right/GuidedStyles', () => ({ GuidedStyles: () => <div data-testid="panel-styles" /> }));
|
||
|
|
|
||
|
|
let container: HTMLDivElement;
|
||
|
|
let root: Root;
|
||
|
|
|
||
|
|
function render(ui: React.ReactElement) {
|
||
|
|
container = document.createElement('div');
|
||
|
|
document.body.appendChild(container);
|
||
|
|
act(() => {
|
||
|
|
root = createRoot(container);
|
||
|
|
root.render(ui);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function unmount() {
|
||
|
|
act(() => {
|
||
|
|
root.unmount();
|
||
|
|
});
|
||
|
|
container.remove();
|
||
|
|
}
|
||
|
|
|
||
|
|
function tabButton(label: string): HTMLButtonElement {
|
||
|
|
const buttons = Array.from(container.querySelectorAll('.mobile-tab-btn')) as HTMLButtonElement[];
|
||
|
|
const btn = buttons.find((b) => b.textContent?.includes(label));
|
||
|
|
if (!btn) throw new Error(`No tab button found for "${label}"`);
|
||
|
|
return btn;
|
||
|
|
}
|
||
|
|
|
||
|
|
function openPanelIds(): string[] {
|
||
|
|
return Array.from(container.querySelectorAll('[data-testid^="panel-"]')).map(
|
||
|
|
(el) => (el as HTMLElement).dataset.testid!,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
describe('MobilePanelBar', () => {
|
||
|
|
test('no sheet is open initially', () => {
|
||
|
|
render(
|
||
|
|
<MobileChromeProvider>
|
||
|
|
<MobilePanelBar />
|
||
|
|
</MobileChromeProvider>,
|
||
|
|
);
|
||
|
|
expect(container.querySelector('.mobile-sheet-backdrop')).toBeNull();
|
||
|
|
expect(openPanelIds()).toEqual([]);
|
||
|
|
unmount();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('tapping a tab opens exactly that tab\'s panel', () => {
|
||
|
|
render(
|
||
|
|
<MobileChromeProvider>
|
||
|
|
<MobilePanelBar />
|
||
|
|
</MobileChromeProvider>,
|
||
|
|
);
|
||
|
|
|
||
|
|
act(() => {
|
||
|
|
tabButton('Blocks').click();
|
||
|
|
});
|
||
|
|
expect(openPanelIds()).toEqual(['panel-blocks']);
|
||
|
|
|
||
|
|
unmount();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('switching tabs replaces the open sheet -- only one panel mounted at a time', () => {
|
||
|
|
render(
|
||
|
|
<MobileChromeProvider>
|
||
|
|
<MobilePanelBar />
|
||
|
|
</MobileChromeProvider>,
|
||
|
|
);
|
||
|
|
|
||
|
|
act(() => {
|
||
|
|
tabButton('Blocks').click();
|
||
|
|
});
|
||
|
|
expect(openPanelIds()).toEqual(['panel-blocks']);
|
||
|
|
|
||
|
|
act(() => {
|
||
|
|
tabButton('Styles').click();
|
||
|
|
});
|
||
|
|
expect(openPanelIds()).toEqual(['panel-styles']);
|
||
|
|
|
||
|
|
act(() => {
|
||
|
|
tabButton('Assets').click();
|
||
|
|
});
|
||
|
|
expect(openPanelIds()).toEqual(['panel-assets']);
|
||
|
|
|
||
|
|
unmount();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('tapping the active tab again closes the sheet', () => {
|
||
|
|
render(
|
||
|
|
<MobileChromeProvider>
|
||
|
|
<MobilePanelBar />
|
||
|
|
</MobileChromeProvider>,
|
||
|
|
);
|
||
|
|
|
||
|
|
act(() => {
|
||
|
|
tabButton('Pages').click();
|
||
|
|
});
|
||
|
|
expect(openPanelIds()).toEqual(['panel-pages']);
|
||
|
|
expect(container.querySelector('.mobile-sheet-backdrop')).not.toBeNull();
|
||
|
|
|
||
|
|
act(() => {
|
||
|
|
tabButton('Pages').click();
|
||
|
|
});
|
||
|
|
expect(openPanelIds()).toEqual([]);
|
||
|
|
expect(container.querySelector('.mobile-sheet-backdrop')).toBeNull();
|
||
|
|
|
||
|
|
unmount();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('active tab button carries aria-pressed=true only for the open sheet', () => {
|
||
|
|
render(
|
||
|
|
<MobileChromeProvider>
|
||
|
|
<MobilePanelBar />
|
||
|
|
</MobileChromeProvider>,
|
||
|
|
);
|
||
|
|
|
||
|
|
act(() => {
|
||
|
|
tabButton('Layers').click();
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(tabButton('Layers').getAttribute('aria-pressed')).toBe('true');
|
||
|
|
expect(tabButton('Blocks').getAttribute('aria-pressed')).toBe('false');
|
||
|
|
|
||
|
|
unmount();
|
||
|
|
});
|
||
|
|
});
|