import { Code } from '@luke-ui/react/code';
import { Text } from '@luke-ui/react/text';
import { test, expect } from 'vite-plus/test';
import { render, visualAppearances } from '../test-utils/render.js';
import { captureVisual, captureVisualAppearance, Stack } from '../test-utils/visual.js';
const LONG_CONTENT =
'one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen';
function getCodeElement(container: HTMLElement): HTMLElement {
const target = container.querySelector('code');
if (!(target instanceof HTMLElement)) throw new Error('Expected a Code element.');
return target;
}
// Line clamp needs the text to wrap to produce more than one line, so the rendered box height is
// the contract here: there is no DOM/ARIA assertion that can distinguish a wrapped, clamped block
// from a single-line one.
test('lineClamp of 2 wraps the content and clamps it to two lines', async () => {
const { container } = render(
{LONG_CONTENT}
,
);
const element = getCodeElement(container);
const singleLineHeight = Number.parseFloat(getComputedStyle(element).lineHeight);
const { height } = element.getBoundingClientRect();
expect(height).toBeGreaterThan(singleLineHeight * 1.5);
// At this width the content wants about seven lines, so an unclamped box would be far taller.
// The bound sits above two padded lines and below three, and `scrollHeight` confirms there is
// still overflowing content for the clamp to cut.
expect(height).toBeLessThan(singleLineHeight * 3);
expect(element.scrollHeight).toBeGreaterThan(height);
});
// `Text` emits `textWrap` after `lineClamp`, so `balance`/`pretty` would otherwise let a
// single-line clamp wrap. Every single-line clamp value is paired with every wrapping value.
for (const lineClamp of [true, 1] as const) {
for (const textWrap of ['balance', 'pretty'] as const) {
test(`lineClamp of ${String(lineClamp)} stays on one line with textWrap ${textWrap}`, async () => {
const { container } = render(
{LONG_CONTENT}
,
);
const element = getCodeElement(container);
const singleLineHeight = Number.parseFloat(getComputedStyle(element).lineHeight);
expect(element.getBoundingClientRect().height).toBeLessThanOrEqual(singleLineHeight * 1.5);
});
}
}
test('textWrap still wraps when no single-line clamp is set', async () => {
const { container } = render(
{LONG_CONTENT}
,
);
const element = getCodeElement(container);
const singleLineHeight = Number.parseFloat(getComputedStyle(element).lineHeight);
expect(element.getBoundingClientRect().height).toBeGreaterThan(singleLineHeight * 1.5);
});
test('default Code stays on one line with the same long content', async () => {
const { container } = render(
{LONG_CONTENT}
,
);
const element = getCodeElement(container);
const singleLineHeight = Number.parseFloat(getComputedStyle(element).lineHeight);
expect(element.getBoundingClientRect().height).toBeLessThanOrEqual(singleLineHeight * 1.5);
});
test('kitchen sink', { tags: ['visual'] }, async () => {
for (const appearance of visualAppearances) {
const { locator } = render(
Run npm install to install dependencies.
The useTheme hook tracks the active theme.
Set data-color-mode on the root element.
,
{ appearance },
);
await captureVisualAppearance(locator, 'code/kitchen-sink', appearance);
}
});
test('long content states', { tags: ['visual'] }, async () => {
const { locator } = render(
{LONG_CONTENT}
{LONG_CONTENT}
,
);
await captureVisual(locator, 'code/long-content');
});