@furystack/shades-common-components
Version:
Common UI components for FuryStack Shades
224 lines • 9.87 kB
JavaScript
import { createComponent, flushUpdates } from '@furystack/shades';
import { describe, expect, it } from 'vitest';
import { Icon } from './icon.js';
const sampleStrokeIcon = {
paths: [{ d: 'M6 6l12 12M18 6L6 18' }],
};
const sampleFillIcon = {
style: 'fill',
paths: [
{ d: 'M12 2l3 6 7 1-5 5 1 7-6-3-6 3 1-7-5-5 7-1z' },
{ d: 'M12 8a4 4 0 100 8 4 4 0 000-8z', fillRule: 'evenodd' },
],
};
const multiPathIcon = {
paths: [{ d: 'M3 12h18' }, { d: 'M3 6h18' }, { d: 'M3 18h18' }],
};
const customViewBoxIcon = {
viewBox: '0 0 48 48',
paths: [{ d: 'M10 10l28 28' }],
};
describe('Icon', () => {
it('should be defined', () => {
expect(Icon).toBeDefined();
expect(typeof Icon).toBe('function');
});
it('should create an icon element', () => {
const el = (createComponent(Icon, { icon: sampleStrokeIcon }));
expect(el).toBeDefined();
expect(el.tagName?.toLowerCase()).toBe('shade-icon');
});
it('should set props correctly', () => {
const el = (createComponent(Icon, { icon: sampleStrokeIcon, size: "large", color: "primary", ariaLabel: "Close" }));
expect(el.props.size).toBe('large');
expect(el.props.color).toBe('primary');
expect(el.props.ariaLabel).toBe('Close');
});
it('should render with default medium size', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
expect(icon.getAttribute('data-size')).toBe('medium');
});
it('should render with small size', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon, size: "small" })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
expect(icon.getAttribute('data-size')).toBe('small');
});
it('should render with large size', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon, size: "large" })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
expect(icon.getAttribute('data-size')).toBe('large');
});
it('should handle custom numeric size', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon, size: 20 })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
expect(icon.hasAttribute('data-size')).toBe(false);
expect(icon.style.width).toBe('20px');
expect(icon.style.height).toBe('20px');
});
it('should set role="img" attribute', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
expect(icon.getAttribute('role')).toBe('img');
});
it('should set aria-label when ariaLabel is provided', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon, ariaLabel: "Close button" })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
expect(icon.getAttribute('aria-label')).toBe('Close button');
expect(icon.hasAttribute('aria-hidden')).toBe(false);
});
it('should set aria-hidden when no ariaLabel is provided', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
expect(icon.getAttribute('aria-hidden')).toBe('true');
expect(icon.hasAttribute('aria-label')).toBe(false);
});
it('should set palette color CSS custom property', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon, color: "error" })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
expect(icon.style.getPropertyValue('--icon-color')).toBe('var(--shades-theme-palette-error-main)');
});
it('should not set --icon-color when no color prop', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
expect(icon.style.getPropertyValue('--icon-color')).toBe('');
});
it('should render an SVG element inside the component', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
const svg = icon.querySelector('svg');
expect(svg).not.toBeNull();
});
it('should render correct number of path elements for stroke icon', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: multiPathIcon })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
const paths = icon.querySelectorAll('path');
expect(paths.length).toBe(3);
});
it('should render correct number of path elements for fill icon', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleFillIcon })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
const paths = icon.querySelectorAll('path');
expect(paths.length).toBe(2);
});
it('should set stroke attributes for stroke-style icons', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
const svg = icon.querySelector('svg');
expect(svg.getAttribute('fill')).toBe('none');
expect(svg.getAttribute('stroke')).toBe('currentColor');
expect(svg.getAttribute('stroke-width')).toBe('2');
expect(svg.getAttribute('stroke-linecap')).toBe('round');
expect(svg.getAttribute('stroke-linejoin')).toBe('round');
});
it('should set fill attributes for fill-style icons', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleFillIcon })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
const svg = icon.querySelector('svg');
expect(svg.getAttribute('fill')).toBe('currentColor');
expect(svg.getAttribute('stroke')).toBe('none');
});
it('should set fill-rule on paths that specify it', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleFillIcon })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
const paths = icon.querySelectorAll('path');
expect(paths[1].getAttribute('fill-rule')).toBe('evenodd');
});
it('should use default viewBox of 0 0 24 24', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
const svg = icon.querySelector('svg');
expect(svg.getAttribute('viewBox')).toBe('0 0 24 24');
});
it('should use custom viewBox when specified', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: customViewBoxIcon })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
const svg = icon.querySelector('svg');
expect(svg.getAttribute('viewBox')).toBe('0 0 48 48');
});
it('should use custom strokeWidth when specified', async () => {
const customStrokeIcon = {
paths: [{ d: 'M6 6l12 12' }],
strokeWidth: 3,
};
const el = (createComponent("div", null,
createComponent(Icon, { icon: customStrokeIcon })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
const svg = icon.querySelector('svg');
expect(svg.getAttribute('stroke-width')).toBe('3');
});
it('should set SVG width and height matching size', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon, size: "large" })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
const svg = icon.querySelector('svg');
expect(svg.getAttribute('width')).toBe('32');
expect(svg.getAttribute('height')).toBe('32');
});
it('should set SVG width and height for custom numeric size', async () => {
const el = (createComponent("div", null,
createComponent(Icon, { icon: sampleStrokeIcon, size: 48 })));
const icon = el.firstElementChild;
icon.updateComponent();
await flushUpdates();
const svg = icon.querySelector('svg');
expect(svg.getAttribute('width')).toBe('48');
expect(svg.getAttribute('height')).toBe('48');
});
});
//# sourceMappingURL=icon.spec.js.map