UNPKG

@furystack/shades-common-components

Version:

Common UI components for FuryStack Shades

199 lines 10 kB
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