Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/lukebennett88/luke-ui. ui.lukebennett.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353import '../styles/app.css';import '@luke-ui/theme-tactile/stylesheet.css';import { Provider } from '@luke-ui/react/provider';import spriteSheetHref from '@luke-ui/react/spritesheet.svg?url&no-inline';import { createMemoryHistory, createRootRoute, createRouter, RouterProvider,} from '@tanstack/react-router';import type { Root as PageTree } from 'fumadocs-core/page-tree';import { RootProvider } from 'fumadocs-ui/provider/tanstack';import type { ReactNode } from 'react';import { act } from 'react';import type { Root } from 'react-dom/client';import { createRoot } from 'react-dom/client';import { afterEach, expect, test } from 'vite-plus/test';import { page, userEvent } from 'vite-plus/test/context';import { DocsShell } from './docs-shell.js';import { DocsSearchProvider } from './search.js';import { DocsThemeRoot } from './theme-controls.js';
const tree: PageTree = { name: 'Docs', children: [ { type: 'folder', name: <span>Documentation</span>, root: true, children: [ { type: 'separator', name: 'Overview' }, { type: 'page', name: 'Installation', url: '/docs/installation' }, { type: 'separator', name: 'Foundations' }, { type: 'page', name: 'Composition', url: '/docs/composition' }, ], }, ], fallback: { name: 'Components', children: [ { type: 'folder', name: 'Components', root: true, index: { type: 'page', name: 'All components', url: '/components' }, children: [ { type: 'separator', name: 'Actions' }, { type: 'page', name: 'Button', url: '/components/actions/button' }, { type: 'page', name: 'Text', url: '/components/typography/text' }, ], }, ], },};
let container: HTMLElement | undefined;let root: Root | undefined;
afterEach(async () => { if (root) act(() => root?.unmount()); container?.remove(); localStorage.clear(); document.documentElement.dir = ''; container = undefined; root = undefined; await page.viewport(1024, 800);});
// Docs size queries are `@container` queries against `:root`. Without that containment they never// match, and every layout silently falls back to its base style.test('contains the root so docs container queries have a container to match', () => { expect(getComputedStyle(document.documentElement).containerType).toBe('inline-size');});
test('keeps every section visible in RTL with plain link semantics', async () => { await page.viewport(1280, 800); document.documentElement.dir = 'rtl'; await renderAt( '/docs/installation', <DocsShell tree={tree}> <main>Article</main> </DocsShell>, );
const navigation = page.getByRole('navigation', { name: 'Docs' }); await expect.element(navigation.getByText('Foundations')).toBeVisible(); await expect.element(navigation.getByText('Actions')).not.toBeInTheDocument(); await expect .element(navigation.getByRole('link', { name: 'Installation' })) .toHaveAttribute('aria-current', 'page'); await expect .element(navigation.getByRole('link', { name: 'Composition' })) .not.toHaveAttribute('aria-current');});
test('groups separator sections into nested lists and moves keyboard focus through the desktop nav with Tab', async () => { await page.viewport(1280, 800); await renderAt( '/docs/installation', <DocsShell tree={tree}> <main>Article</main> </DocsShell>, );
const navigation = page.getByRole('navigation', { name: 'Docs' }); const installation = navigation.getByRole('link', { name: 'Installation' }); const overview = navigation.getByRole('heading', { name: 'Overview', level: 2 }); const foundations = navigation.getByRole('heading', { name: 'Foundations', level: 2 }); await expect.element(overview).toBeVisible(); await expect.element(foundations).toBeVisible(); await expect.element(navigation.getByText('Documentation')).not.toBeInTheDocument(); await expect.element(navigation.getByText('Actions')).not.toBeInTheDocument(); await expect.element(navigation.getByRole('link', { name: 'Button' })).not.toBeInTheDocument(); await expect.element(navigation.getByRole('button')).not.toBeInTheDocument();
const overviewList = navigation .element() .querySelector(`ul[aria-labelledby="${overview.element().id}"]`); expect(overviewList).not.toBeNull(); expect( Array.from(overviewList!.querySelectorAll(':scope > li > a'), (link) => link.textContent), ).toEqual(['Installation']); const foundationsList = navigation .element() .querySelector(`ul[aria-labelledby="${foundations.element().id}"]`); expect(foundationsList).not.toBeNull(); expect( Array.from(foundationsList!.querySelectorAll(':scope > li > a'), (link) => link.textContent), ).toEqual(['Composition']);
expect( Array.from(navigation.element().querySelectorAll('a'), (link) => link.textContent), ).toEqual(['Installation', 'Composition']); await expect.element(installation).toHaveAttribute('aria-current', 'page'); await act(async () => { installation.element().focus(); }); await act(async () => { await userEvent.tab(); }); await expect.element(navigation.getByRole('link', { name: 'Composition' })).toHaveFocus();});
test('shows the Documentation section at the docs root', async () => { await page.viewport(1280, 800); await renderAt( '/docs', <DocsShell tree={tree}> <main>Article</main> </DocsShell>, );
const navigation = page.getByRole('navigation', { name: 'Docs' }); await expect.element(navigation.getByRole('link', { name: 'Installation' })).toBeVisible(); await expect .element(navigation.getByRole('link', { name: 'All components' })) .not.toBeInTheDocument();});
for (const pathname of ['/components', '/components/actions/button']) { test(`renders only the Components section at ${pathname}`, async () => { await page.viewport(1280, 800); await renderAt( pathname, <DocsShell tree={tree}> <main>Article</main> </DocsShell>, );
const navigation = page.getByRole('navigation', { name: 'Docs' }); const actions = navigation.getByRole('heading', { name: 'Actions', level: 2 }); await expect.element(actions).toBeVisible(); await expect .element(navigation.getByRole('link', { name: 'Installation' })) .not.toBeInTheDocument(); const actionsList = navigation .element() .querySelector(`ul[aria-labelledby="${actions.element().id}"]`); expect(actionsList).not.toBeNull(); expect( Array.from(actionsList!.querySelectorAll(':scope > li > a'), (link) => link.textContent), ).toEqual(['Button', 'Text']); expect( Array.from(navigation.element().querySelectorAll('a'), (link) => link.textContent), ).toEqual(['All components', 'Button', 'Text']); await expect .element( navigation.getByRole('link', { name: pathname === '/components' ? 'All components' : 'Button', }), ) .toHaveAttribute('aria-current', 'page'); expect( navigation .getByRole('link', { name: 'All components' }) .element() .getAttribute('aria-current'), ).toBe(pathname === '/components' ? 'page' : null); });}
test('opens the mobile drawer, exposes site and docs navigation, and restores focus on Escape', async () => { await page.viewport(390, 800); await renderAt( '/docs/installation', <DocsShell tree={tree}> <main>Article</main> </DocsShell>, );
const trigger = page.getByRole('button', { name: 'Menu' }); await act(async () => { await userEvent.click(trigger); }); await expect.element(page.getByRole('dialog', { name: 'Docs navigation' })).toBeVisible(); await expect .element( page.getByRole('navigation', { name: 'Site' }).getByRole('link', { name: 'Playground' }), ) .toBeVisible(); await expect .element(page.getByRole('dialog').getByRole('link', { name: 'Installation' })) .toBeVisible(); await expect .element(page.getByRole('dialog').getByRole('link', { name: 'Button' })) .not.toBeInTheDocument(); await act(async () => { await userEvent.keyboard('{Escape}'); }); await expect.element(page.getByRole('dialog')).not.toBeInTheDocument(); await expect.element(trigger).toHaveFocus();});
test('closes the mobile drawer from its close button and restores focus', async () => { await page.viewport(390, 800); await renderAt( '/docs/installation', <DocsShell tree={tree}> <main>Article</main> </DocsShell>, );
const trigger = page.getByRole('button', { name: 'Menu' }); await act(async () => { await userEvent.click(trigger); }); await expect.element(page.getByRole('dialog', { name: 'Docs navigation' })).toBeVisible(); await act(async () => { await userEvent.click(page.getByRole('button', { name: 'Close docs navigation' })); }); await expect.element(page.getByRole('dialog')).not.toBeInTheDocument(); await expect.element(trigger).toHaveFocus();});
test('closes the mobile drawer when a docs nav link is activated', async () => { await page.viewport(390, 800); await renderAt( '/docs/installation', <DocsShell tree={tree}> <main>Article</main> </DocsShell>, );
await act(async () => { await userEvent.click(page.getByRole('button', { name: 'Menu' })); }); const dialog = page.getByRole('dialog', { name: 'Docs navigation' }); await expect.element(dialog).toBeVisible(); await act(async () => { await userEvent.click(dialog.getByRole('link', { name: 'Composition' })); }); await expect.element(page.getByRole('dialog')).not.toBeInTheDocument();});
test('shows the Components section in the mobile drawer', async () => { await page.viewport(390, 800); await renderAt( '/components/actions/button', <DocsShell tree={tree}> <main>Article</main> </DocsShell>, );
await act(async () => { await userEvent.click(page.getByRole('button', { name: 'Menu' })); }); const dialog = page.getByRole('dialog', { name: 'Docs navigation' }); await expect.element(dialog.getByRole('link', { name: 'All components' })).toBeVisible(); await expect .element(dialog.getByRole('link', { name: 'Button' })) .toHaveAttribute('aria-current', 'page'); await expect.element(dialog.getByRole('link', { name: 'Installation' })).not.toBeInTheDocument();});
test('closes the mobile drawer when the viewport crosses into the desktop layout', async () => { await page.viewport(390, 800); await renderAt( '/docs/installation', <DocsShell tree={tree}> <main>Article</main> </DocsShell>, );
await act(async () => { await userEvent.click(page.getByRole('button', { name: 'Menu' })); }); await expect.element(page.getByRole('dialog', { name: 'Docs navigation' })).toBeVisible();
await act(async () => { await page.viewport(1280, 800); });
await expect .element(page.getByRole('dialog', { name: 'Docs navigation' })) .not.toBeInTheDocument(); const navigation = page.getByRole('navigation', { name: 'Docs' }); await expect.element(navigation.getByRole('link', { name: 'Installation' })).toBeVisible(); await expect .element(navigation.getByRole('link', { name: 'Installation' })) .toHaveAttribute('aria-current', 'page'); await act(async () => { navigation.getByRole('link', { name: 'Installation' }).element().focus(); }); await act(async () => { await userEvent.tab(); }); await expect.element(navigation.getByRole('link', { name: 'Composition' })).toHaveFocus();});
async function renderAt(pathname: string, children: ReactNode) { const rootRoute = createRootRoute({ component: () => ( <RootProvider search={{ enabled: false }} theme={{ enabled: false }}> <DocsSearchProvider> <Provider spritesheetHref={spriteSheetHref}> <DocsThemeRoot>{children}</DocsThemeRoot> </Provider> </DocsSearchProvider> </RootProvider> ), }); const router = createRouter({ history: createMemoryHistory({ initialEntries: [pathname] }), routeTree: rootRoute, }); container = document.body.appendChild(document.createElement('div')); root = createRoot(container); await act(async () => { root?.render(<RouterProvider router={router} />); await router.load(); });}