@furystack/shades-common-components
Version:
Common UI components for FuryStack Shades
199 lines • 10 kB
JavaScript
import { createInjector } from '@furystack/inject';
import { createComponent, flushUpdates, initializeShadeRoot } from '@furystack/shades';
import { usingAsync } from '@furystack/utils';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { Typography } from './typography.js';
describe('Typography', () => {
beforeEach(() => {
document.body.innerHTML = '<div id="root"></div>';
});
afterEach(() => {
document.body.innerHTML = '';
});
const renderTypography = async (props = {}, children) => {
const injector = createInjector();
const root = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement: root,
jsxElement: createComponent(Typography, { ...props }, children),
});
await flushUpdates();
return {
injector,
element: root.querySelector('[is^="shade-typography"]'),
[Symbol.asyncDispose]: () => injector[Symbol.asyncDispose](),
};
};
describe('rendering', () => {
it('should render a customized built-in element', async () => {
await usingAsync(await renderTypography(), async ({ element }) => {
expect(element).toBeTruthy();
expect(element.tagName.toLowerCase()).toBe('p');
expect(element.getAttribute('is')).toBe('shade-typography-p');
});
});
it('should render children text', async () => {
await usingAsync(await renderTypography({}, ['Hello World']), async ({ element }) => {
expect(element.textContent).toContain('Hello World');
});
});
});
describe('variants', () => {
const headingVariants = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
const bodyVariants = ['body1', 'body2'];
const subtitleVariants = ['subtitle1', 'subtitle2'];
const inlineVariants = ['caption', 'overline'];
it('should default to body1 variant with p tag', async () => {
await usingAsync(await renderTypography(), async ({ element }) => {
expect(element.getAttribute('data-variant')).toBe('body1');
expect(element.tagName.toLowerCase()).toBe('p');
expect(element.getAttribute('is')).toBe('shade-typography-p');
});
});
headingVariants.forEach((variant) => {
it(`should render ${variant} tag for variant="${variant}"`, async () => {
await usingAsync(await renderTypography({ variant }), async ({ element }) => {
expect(element.getAttribute('data-variant')).toBe(variant);
expect(element.tagName.toLowerCase()).toBe(variant);
expect(element.getAttribute('is')).toBe(`shade-typography-${variant}`);
});
});
});
bodyVariants.forEach((variant) => {
it(`should render p tag for variant="${variant}"`, async () => {
await usingAsync(await renderTypography({ variant }), async ({ element }) => {
expect(element.tagName.toLowerCase()).toBe('p');
expect(element.getAttribute('is')).toBe('shade-typography-p');
});
});
});
subtitleVariants.forEach((variant) => {
it(`should render h6 tag for variant="${variant}"`, async () => {
await usingAsync(await renderTypography({ variant }), async ({ element }) => {
expect(element.tagName.toLowerCase()).toBe('h6');
expect(element.getAttribute('is')).toBe('shade-typography-h6');
});
});
});
inlineVariants.forEach((variant) => {
it(`should render span tag for variant="${variant}"`, async () => {
await usingAsync(await renderTypography({ variant }), async ({ element }) => {
expect(element.tagName.toLowerCase()).toBe('span');
expect(element.getAttribute('is')).toBe('shade-typography-span');
});
});
});
});
describe('colors', () => {
it('should use textPrimary color by default', async () => {
await usingAsync(await renderTypography(), async ({ element }) => {
expect(element.style.getPropertyValue('--typo-color')).toBe('var(--shades-theme-text-primary)');
});
});
it('should set textSecondary color', async () => {
await usingAsync(await renderTypography({ color: 'textSecondary' }), async ({ element }) => {
expect(element.style.getPropertyValue('--typo-color')).toBe('var(--shades-theme-text-secondary)');
});
});
it('should set textDisabled color', async () => {
await usingAsync(await renderTypography({ color: 'textDisabled' }), async ({ element }) => {
expect(element.style.getPropertyValue('--typo-color')).toBe('var(--shades-theme-text-disabled)');
});
});
it('should set palette primary color', async () => {
await usingAsync(await renderTypography({ color: 'primary' }), async ({ element }) => {
expect(element.style.getPropertyValue('--typo-color')).toBe('var(--shades-theme-palette-primary-main)');
});
});
it('should set palette error color', async () => {
await usingAsync(await renderTypography({ color: 'error' }), async ({ element }) => {
expect(element.style.getPropertyValue('--typo-color')).toBe('var(--shades-theme-palette-error-main)');
});
});
});
describe('ellipsis', () => {
it('should set data-ellipsis="true" for boolean ellipsis', async () => {
await usingAsync(await renderTypography({ ellipsis: true }), async ({ element }) => {
expect(element.getAttribute('data-ellipsis')).toBe('true');
});
});
it('should set data-ellipsis="multiline" for numeric ellipsis', async () => {
await usingAsync(await renderTypography({ ellipsis: 3 }), async ({ element }) => {
expect(element.getAttribute('data-ellipsis')).toBe('multiline');
expect(element.style.webkitLineClamp).toBe('3');
});
});
it('should not set data-ellipsis when not specified', async () => {
await usingAsync(await renderTypography(), async ({ element }) => {
expect(element.getAttribute('data-ellipsis')).toBeNull();
});
});
});
describe('copyable', () => {
it('should render copy button when copyable is true', async () => {
await usingAsync(await renderTypography({ copyable: true }, ['Copy me']), async ({ element }) => {
const copyBtn = element.querySelector('.typo-copy-btn');
expect(copyBtn).toBeTruthy();
});
});
it('should not render copy button when copyable is false or undefined', async () => {
await usingAsync(await renderTypography(), async ({ element }) => {
const copyBtn = element.querySelector('.typo-copy-btn');
expect(copyBtn).toBeNull();
});
});
it('should call clipboard API when copy button is clicked', async () => {
const writeText = vi.fn().mockResolvedValue(undefined);
Object.assign(navigator, { clipboard: { writeText } });
await usingAsync(await renderTypography({ copyable: true }, ['Copy this text']), async ({ element }) => {
const copyBtn = element.querySelector('.typo-copy-btn');
copyBtn.click();
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('Copy this text'));
});
});
});
describe('gutterBottom', () => {
it('should set data-gutter-bottom attribute when gutterBottom is true', async () => {
await usingAsync(await renderTypography({ gutterBottom: true }), async ({ element }) => {
expect(element.hasAttribute('data-gutter-bottom')).toBe(true);
});
});
it('should not set data-gutter-bottom attribute when gutterBottom is falsy', async () => {
await usingAsync(await renderTypography(), async ({ element }) => {
expect(element.hasAttribute('data-gutter-bottom')).toBe(false);
});
});
});
describe('align', () => {
const alignments = ['left', 'center', 'right', 'justify'];
alignments.forEach((align) => {
it(`should set data-align="${align}"`, async () => {
await usingAsync(await renderTypography({ align }), async ({ element }) => {
expect(element.getAttribute('data-align')).toBe(align);
});
});
});
it('should not set data-align when not specified', async () => {
await usingAsync(await renderTypography(), async ({ element }) => {
expect(element.getAttribute('data-align')).toBeNull();
});
});
});
describe('custom styles', () => {
it('should apply custom styles from style prop', async () => {
await usingAsync(await renderTypography({ style: { margin: '20px', padding: '10px' } }), async ({ element }) => {
expect(element.style.margin).toBe('20px');
expect(element.style.padding).toBe('10px');
});
});
});
describe('overline variant', () => {
it('should apply uppercase text transform for overline', async () => {
await usingAsync(await renderTypography({ variant: 'overline' }), async ({ element }) => {
expect(element.getAttribute('data-variant')).toBe('overline');
});
});
});
});
//# sourceMappingURL=typography.spec.js.map