@furystack/shades-common-components
Version:
Common UI components for FuryStack Shades
165 lines • 6.69 kB
JavaScript
import { createInjector } from '@furystack/inject';
import { createComponent, flushUpdates, initializeShadeRoot, SpatialNavigationService } from '@furystack/shades';
import { usingAsync } from '@furystack/utils';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { ConfirmDialog, Dialog } from './dialog.js';
describe('Dialog', () => {
beforeEach(() => {
document.body.innerHTML = '<div id="root"></div>';
});
afterEach(() => {
document.body.innerHTML = '';
});
it('should be defined', () => {
expect(Dialog).toBeDefined();
expect(typeof Dialog).toBe('function');
});
it('should create a dialog element', () => {
const el = (createComponent(Dialog, { isVisible: true }));
expect(el).toBeDefined();
expect(el.tagName?.toLowerCase()).toBe('shade-dialog');
});
it('should pass title prop', () => {
const el = (createComponent(Dialog, { isVisible: true, title: "Test Title" }));
expect(el.props.title).toBe('Test Title');
});
it('should pass onClose prop', () => {
const onClose = vi.fn();
const el = (createComponent(Dialog, { isVisible: true, onClose: onClose }));
expect(el.props.onClose).toBe(onClose);
});
it('should pass actions prop', () => {
const actions = createComponent("button", null, "OK");
const el = (createComponent(Dialog, { isVisible: true, actions: actions }));
expect(el.props.actions).toBeDefined();
});
it('should pass maxWidth prop', () => {
const el = (createComponent(Dialog, { isVisible: true, maxWidth: "800px" }));
expect(el.props.maxWidth).toBe('800px');
});
it('should pass fullWidth prop', () => {
const el = (createComponent(Dialog, { isVisible: true, fullWidth: true }));
expect(el.props.fullWidth).toBe(true);
});
it('should pass children as dialog content', () => {
const el = (createComponent(Dialog, { isVisible: true },
createComponent("p", null, "Dialog content")));
expect(el).toBeDefined();
});
it('should accept isVisible boolean', () => {
const el = (createComponent(Dialog, { isVisible: false }));
expect(el.props.isVisible).toBe(false);
});
it('should forward trapFocus to the underlying Modal', async () => {
await usingAsync(createInjector(), async (injector) => {
const spatialNav = injector.get(SpatialNavigationService);
const pushSpy = vi.spyOn(spatialNav, 'pushFocusTrap');
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Dialog, { isVisible: true, trapFocus: true, navSection: "dialog-trap" },
createComponent("div", null, "Content"))),
});
await flushUpdates();
expect(pushSpy).toHaveBeenCalledWith('dialog-trap');
});
});
it('should forward navSection to the underlying Modal', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Dialog, { isVisible: true, navSection: "my-dialog" },
createComponent("div", null, "Content"))),
});
await flushUpdates();
const backdrop = document.querySelector('.shade-backdrop');
expect(backdrop?.getAttribute('data-nav-section')).toBe('my-dialog');
});
});
it('should not push focus trap when trapFocus is false', async () => {
await usingAsync(createInjector(), async (injector) => {
const spatialNav = injector.get(SpatialNavigationService);
const pushSpy = vi.spyOn(spatialNav, 'pushFocusTrap');
const rootElement = document.getElementById('root');
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Dialog, { isVisible: true, trapFocus: false },
createComponent("div", null, "Content"))),
});
await flushUpdates();
expect(pushSpy).not.toHaveBeenCalled();
});
});
});
describe('ConfirmDialog', () => {
it('should be defined', () => {
expect(ConfirmDialog).toBeDefined();
expect(typeof ConfirmDialog).toBe('function');
});
it('should create a confirm dialog element', () => {
const onConfirm = vi.fn();
const el = ConfirmDialog(true, {
title: 'Confirm',
message: 'Are you sure?',
onConfirm,
});
expect(el).toBeDefined();
expect(el.tagName?.toLowerCase()).toBe('shade-dialog');
});
it('should pass the title to the dialog', () => {
const onConfirm = vi.fn();
const el = ConfirmDialog(true, {
title: 'Delete Item',
message: 'Are you sure?',
onConfirm,
});
expect(el.props.title).toBe('Delete Item');
});
it('should render with default button texts', () => {
const onConfirm = vi.fn();
const el = ConfirmDialog(true, {
title: 'Confirm',
message: 'Are you sure?',
onConfirm,
});
expect(el.props.actions).toBeDefined();
});
it('should accept custom button texts', () => {
const onConfirm = vi.fn();
const el = ConfirmDialog(true, {
title: 'Confirm',
message: 'Are you sure?',
confirmText: 'Yes, delete',
cancelText: 'No, keep',
onConfirm,
});
expect(el).toBeDefined();
});
it('should accept JSX as message', () => {
const onConfirm = vi.fn();
const el = ConfirmDialog(true, {
title: 'Confirm',
message: (createComponent("div", null,
createComponent("strong", null, "Warning:"),
" This action cannot be undone.")),
onConfirm,
});
expect(el).toBeDefined();
});
it('should call onCancel when onClose is triggered', () => {
const onCancel = vi.fn();
const el = ConfirmDialog(true, {
title: 'Confirm',
message: 'Are you sure?',
onConfirm: vi.fn(),
onCancel,
});
el.props.onClose?.();
expect(onCancel).toHaveBeenCalledOnce();
});
});
//# sourceMappingURL=dialog.spec.js.map