@furystack/shades-common-components
Version:
Common UI components for FuryStack Shades
254 lines • 11.7 kB
JavaScript
import { createInjector } from '@furystack/inject';
import { createComponent, initializeShadeRoot, Shade } from '@furystack/shades';
import { ObservableValue, sleepAsync, usingAsync } from '@furystack/utils';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { ThemeProviderService } from '../services/theme-provider-service.js';
import { LinearProgress } from './linear-progress.js';
const ProgressWrapper = Shade({
customElementName: 'test-linear-progress-wrapper',
render: ({ props, useObservable }) => {
const [value] = useObservable('value', props.obs);
return createComponent(LinearProgress, { variant: "determinate", value: value });
},
});
describe('LinearProgress', () => {
let originalAnimate;
beforeEach(() => {
document.body.innerHTML = '<div id="root"></div>';
originalAnimate = Element.prototype.animate;
Element.prototype.animate = vi.fn((_keyframes, _options) => {
const mockAnimation = {
onfinish: null,
oncancel: null,
cancel: vi.fn(),
play: vi.fn(),
pause: vi.fn(),
finish: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
};
return mockAnimation;
});
});
afterEach(() => {
document.body.innerHTML = '';
Element.prototype.animate = originalAnimate;
vi.restoreAllMocks();
});
it('should render as custom element', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(LinearProgress, null),
});
await sleepAsync(50);
const el = document.querySelector('shade-linear-progress');
expect(el).not.toBeNull();
});
});
it('should render the progress bar element', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(LinearProgress, null),
});
await sleepAsync(50);
const bar = document.querySelector('shade-linear-progress .progress-bar');
expect(bar).not.toBeNull();
});
});
it('should set role="progressbar"', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(LinearProgress, null),
});
await sleepAsync(50);
const el = document.querySelector('shade-linear-progress');
expect(el.getAttribute('role')).toBe('progressbar');
});
});
describe('determinate variant', () => {
it('should set aria-valuenow for determinate variant', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(LinearProgress, { variant: "determinate", value: 50 }),
});
await sleepAsync(50);
const el = document.querySelector('shade-linear-progress');
expect(el.getAttribute('aria-valuenow')).toBe('50');
expect(el.getAttribute('aria-valuemin')).toBe('0');
expect(el.getAttribute('aria-valuemax')).toBe('100');
});
});
it('should set bar width based on value', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(LinearProgress, { variant: "determinate", value: 75 }),
});
await sleepAsync(50);
const bar = document.querySelector('shade-linear-progress .progress-bar');
expect(bar.style.width).toBe('75%');
});
});
it('should clamp value to 0-100 range', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(LinearProgress, { variant: "determinate", value: 150 }),
});
await sleepAsync(50);
const bar = document.querySelector('shade-linear-progress .progress-bar');
expect(bar.style.width).toBe('100%');
});
});
it('should clamp negative values to 0', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(LinearProgress, { variant: "determinate", value: -20 }),
});
await sleepAsync(50);
const bar = document.querySelector('shade-linear-progress .progress-bar');
expect(bar.style.width).toBe('0%');
});
});
it('should update bar width when value prop changes', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const obs = new ObservableValue(20);
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(ProgressWrapper, { obs: obs }),
});
await sleepAsync(50);
const bar = document.querySelector('shade-linear-progress .progress-bar');
expect(bar.style.width).toBe('20%');
obs.setValue(80);
await sleepAsync(50);
expect(bar.style.width).toBe('80%');
});
});
it('should update aria-valuenow when value prop changes', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const obs = new ObservableValue(30);
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(ProgressWrapper, { obs: obs }),
});
await sleepAsync(50);
const el = document.querySelector('shade-linear-progress');
expect(el.getAttribute('aria-valuenow')).toBe('30');
obs.setValue(90);
await sleepAsync(50);
expect(el.getAttribute('aria-valuenow')).toBe('90');
});
});
});
describe('indeterminate variant', () => {
it('should default to indeterminate variant', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(LinearProgress, null),
});
await sleepAsync(50);
const bar = document.querySelector('shade-linear-progress .progress-bar');
expect(bar.hasAttribute('data-indeterminate')).toBe(true);
});
});
it('should not set aria-valuenow for indeterminate variant', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(LinearProgress, null),
});
await sleepAsync(50);
const el = document.querySelector('shade-linear-progress');
expect(el.hasAttribute('aria-valuenow')).toBe(false);
});
});
});
describe('size', () => {
it('should set data-size="small" when size is small', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(LinearProgress, { size: "small" }),
});
await sleepAsync(50);
const el = document.querySelector('shade-linear-progress');
expect(el.getAttribute('data-size')).toBe('small');
});
});
it('should not set data-size when size is medium (default)', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(LinearProgress, null),
});
await sleepAsync(50);
const el = document.querySelector('shade-linear-progress');
expect(el.hasAttribute('data-size')).toBe(false);
});
});
});
describe('theme integration', () => {
it('should set CSS color variable from theme', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(LinearProgress, null),
});
await sleepAsync(50);
const el = document.querySelector('shade-linear-progress');
const themeService = injector.get(ThemeProviderService);
expect(el.style.getPropertyValue('--progress-color')).toBe(themeService.theme.palette.primary.main);
});
});
it('should use custom color from color prop', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: createComponent(LinearProgress, { color: "error" }),
});
await sleepAsync(50);
const el = document.querySelector('shade-linear-progress');
const themeService = injector.get(ThemeProviderService);
expect(el.style.getPropertyValue('--progress-color')).toBe(themeService.theme.palette.error.main);
});
});
});
});
//# sourceMappingURL=linear-progress.spec.js.map