UNPKG

@furystack/shades-common-components

Version:

Common UI components for FuryStack Shades

165 lines 6.69 kB
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