import '../styles/app.css'; import '@luke-ui/theme-tactile/stylesheet.css'; import { Button } from '@luke-ui/react/button'; import { Grid } from '@luke-ui/react/grid'; import { Stack } from '@luke-ui/react/stack'; import { act } from 'react'; import type { ComponentProps, ReactNode } from 'react'; import type { Root } from 'react-dom/client'; import { createRoot } from 'react-dom/client'; import { afterEach, expect, test } from 'vite-plus/test'; import { Comparison, ComparisonItem } from '#docs'; import { StoryWrapper } from './story-wrapper.js'; const mounted: Array<{ container: HTMLElement; root: Root }> = []; afterEach(() => { for (const entry of mounted) { act(() => entry.root.unmount()); entry.container.remove(); } mounted.length = 0; }); test('default and explicit flow give block roots the available width', () => { for (const layout of [undefined, 'flow'] as const) { const { available, exampleRoot, storyRoot } = renderInWrapper(
Block content
, { layout, }); expect(getComputedStyle(storyRoot).display).toBe('block'); expect(exampleRoot.getBoundingClientRect().width).toBeCloseTo(available, 0); } }); test('flow gives an auto-fit Grid multiple tracks without explicit width', () => { const { available, exampleRoot } = renderInWrapper( First Second Third Fourth , ); expect(exampleRoot.getBoundingClientRect().width).toBeCloseTo(available, 0); expect(getComputedStyle(exampleRoot).gridTemplateColumns.split(' ').filter(Boolean).length).toBe( 4, ); }); test('flow gives an unsized form and nested Stack the available width', () => { const { available, exampleRoot } = renderInWrapper(
, ); expect(exampleRoot.getBoundingClientRect().width).toBeCloseTo(available, 0); expect(exampleRoot.firstElementChild?.getBoundingClientRect().width).toBeCloseTo(available, 0); expect(getComputedStyle(exampleRoot.querySelector('label') as HTMLElement).textAlign).toBe( 'start', ); }); test('centered keeps an intrinsic Button centred', () => { const { available, exampleRoot, storyRoot } = renderInWrapper(, { layout: 'centered', }); const storyBox = storyRoot.getBoundingClientRect(); const buttonBox = exampleRoot.getBoundingClientRect(); expect(getComputedStyle(storyRoot).display).toBe('flex'); expect(buttonBox.width).toBeLessThan(available / 2); expect( Math.abs(buttonBox.left - storyBox.left - (storyBox.right - buttonBox.right)), ).toBeLessThanOrEqual(1); }); test('centered intentionally keeps an unsized auto-fit Grid intrinsic', () => { const { available, exampleRoot } = renderInWrapper( First Second , { layout: 'centered' }, ); expect(exampleRoot.getBoundingClientRect().width).toBeLessThan(available); expect(getComputedStyle(exampleRoot).gridTemplateColumns.split(' ').filter(Boolean).length).toBe( 1, ); }); test('flow respects a max-inline-size cap without requiring full width', () => { const wide = renderInWrapper( , ); const narrow = renderInWrapper( , { containerWidth: 300 }, ); expect(wide.exampleRoot.getBoundingClientRect().width).toBeCloseTo(320, 0); expect(narrow.exampleRoot.getBoundingClientRect().width).toBeCloseTo(narrow.available, 0); }); test('flow retains deliberately narrow example sizing', () => { const { exampleRoot } = renderInWrapper(
First Second
, ); const grid = exampleRoot.firstElementChild as HTMLElement; expect(exampleRoot.getBoundingClientRect().width).toBeCloseTo(160, 0); expect(getComputedStyle(grid).gridTemplateColumns.split(' ').filter(Boolean).length).toBe(1); }); for (const comparisonCase of [ { containerWidth: undefined as number | undefined, expectCompact: true, name: 'wide' }, { containerWidth: 280, expectCompact: false, name: 'narrow' }, ]) { test(`Comparison stays within the ${comparisonCase.name} flow surface and centred`, () => { const { available, exampleRoot, storyRoot } = renderInWrapper(comparisonFixture(), { containerWidth: comparisonCase.containerWidth, }); const storyBox = storyRoot.getBoundingClientRect(); const comparisonBox = exampleRoot.getBoundingClientRect(); expect(comparisonBox.width <= available).toBe(true); expect(comparisonBox.width < available || !comparisonCase.expectCompact).toBe(true); expect(comparisonBox.left).toBeGreaterThanOrEqual(storyBox.left - 1); expect(comparisonBox.right).toBeLessThanOrEqual(storyBox.right + 1); expect( Math.abs(comparisonBox.left - storyBox.left - (storyBox.right - comparisonBox.right)), ).toBeLessThanOrEqual(1); }); } test('full-bleed has no inset padding or minimum height', () => { const { exampleRoot, storyRoot } = renderInWrapper(
Full-bleed content
, { layout: 'full-bleed' }, ); expect(getComputedStyle(storyRoot).padding).toBe('0px'); expect(getComputedStyle(storyRoot).display).toBe('block'); expect(storyRoot.getBoundingClientRect().height).toBeCloseTo( exampleRoot.getBoundingClientRect().height, 0, ); }); function comparisonFixture() { return ( ); } function renderInWrapper( node: ReactNode, { containerWidth = 900, layout, }: { containerWidth?: number; layout?: ComponentProps['layout']; } = {}, ) { const container = document.createElement('div'); container.style.cssText = `container-type: inline-size; inline-size: ${containerWidth}px;`; document.body.append(container); const root = createRoot(container); mounted.push({ container, root }); act(() => { root.render({node}); }); const storyRoot = container.firstElementChild as HTMLElement; const exampleRoot = storyRoot.firstElementChild as HTMLElement; const style = getComputedStyle(storyRoot); const available = storyRoot.getBoundingClientRect().width - (Number.parseFloat(style.paddingLeft) + Number.parseFloat(style.paddingRight)); return { available, exampleRoot, storyRoot }; }