UNPKG

@furystack/shades-common-components

Version:

Common UI components for FuryStack Shades

238 lines 11.8 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 { CollectionService } from '../../services/collection-service.js'; import { DataGridBody } from './body.js'; describe('DataGridBody', () => { beforeEach(() => { document.body.innerHTML = '<div id="root"></div>'; }); afterEach(() => { document.body.innerHTML = ''; }); it('should render default empty component when no data', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(new CollectionService(), async (service) => { const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: (createComponent("table", null, createComponent(DataGridBody, { service: service, columns: ['id', 'name'] }))), }); await flushUpdates(); const body = document.querySelector('tbody[is="shade-data-grid-body"]'); expect(body).not.toBeNull(); expect(body?.textContent).toContain('- No Data -'); }); }); }); it('should render custom empty component when provided and no data', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(new CollectionService(), async (service) => { const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: (createComponent("table", null, createComponent(DataGridBody, { service: service, columns: ['id', 'name'], emptyComponent: createComponent("div", { "data-testid": "custom-empty" }, "Custom Empty State") }))), }); await flushUpdates(); const body = document.querySelector('tbody[is="shade-data-grid-body"]'); expect(body).not.toBeNull(); expect(body?.querySelector('[data-testid="custom-empty"]')).not.toBeNull(); expect(body?.textContent).toContain('Custom Empty State'); }); }); }); it('should render rows for each data entry', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(new CollectionService(), async (service) => { const rootElement = document.getElementById('root'); service.data.setValue({ count: 2, entries: [ { id: 1, name: 'First' }, { id: 2, name: 'Second' }, ], }); initializeShadeRoot({ injector, rootElement, jsxElement: (createComponent("table", null, createComponent(DataGridBody, { service: service, columns: ['id', 'name'] }))), }); await flushUpdates(); const body = document.querySelector('tbody[is="shade-data-grid-body"]'); expect(body).not.toBeNull(); const rows = body?.querySelectorAll('shades-data-grid-row'); expect(rows?.length).toBe(2); }); }); }); it('should render cell content from entry properties', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(new CollectionService(), async (service) => { const rootElement = document.getElementById('root'); service.data.setValue({ count: 1, entries: [{ id: 42, name: 'Test Entry' }], }); initializeShadeRoot({ injector, rootElement, jsxElement: (createComponent("table", null, createComponent(DataGridBody, { service: service, columns: ['id', 'name'] }))), }); await flushUpdates(); const body = document.querySelector('tbody[is="shade-data-grid-body"]'); const cells = body?.querySelectorAll('td'); expect(cells?.length).toBe(2); expect(cells?.[0]?.textContent).toBe('42'); expect(cells?.[1]?.textContent).toBe('Test Entry'); }); }); }); it('should re-render when data observable changes', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(new CollectionService(), async (service) => { const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: (createComponent("table", null, createComponent(DataGridBody, { service: service, columns: ['id', 'name'] }))), }); await flushUpdates(); let body = document.querySelector('tbody[is="shade-data-grid-body"]'); expect(body?.textContent).toContain('- No Data -'); service.data.setValue({ count: 1, entries: [{ id: 1, name: 'New Entry' }], }); await flushUpdates(); body = document.querySelector('tbody[is="shade-data-grid-body"]'); const rows = body?.querySelectorAll('shades-data-grid-row'); expect(rows?.length).toBe(1); }); }); }); it('should call onRowClick callback when row is clicked', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(new CollectionService(), async (service) => { const rootElement = document.getElementById('root'); const onRowClick = vi.fn(); const entry = { id: 1, name: 'Clickable' }; service.data.setValue({ count: 1, entries: [entry], }); initializeShadeRoot({ injector, rootElement, jsxElement: (createComponent("table", null, createComponent(DataGridBody, { service: service, columns: ['id', 'name'], onRowClick: onRowClick }))), }); await flushUpdates(); const cell = document.querySelector('td'); cell.click(); expect(onRowClick).toHaveBeenCalledWith(entry, expect.any(MouseEvent)); }); }); }); it('should call onRowDoubleClick callback when row is double-clicked', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(new CollectionService(), async (service) => { const rootElement = document.getElementById('root'); const onRowDoubleClick = vi.fn(); const entry = { id: 1, name: 'DoubleClickable' }; service.data.setValue({ count: 1, entries: [entry], }); initializeShadeRoot({ injector, rootElement, jsxElement: (createComponent("table", null, createComponent(DataGridBody, { service: service, columns: ['id', 'name'], onRowDoubleClick: onRowDoubleClick }))), }); await flushUpdates(); const cell = document.querySelector('td'); const dblClickEvent = new MouseEvent('dblclick', { bubbles: true }); cell.dispatchEvent(dblClickEvent); expect(onRowDoubleClick).toHaveBeenCalledWith(entry, expect.any(MouseEvent)); }); }); }); it('should use custom row components when provided', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(new CollectionService(), async (service) => { const rootElement = document.getElementById('root'); service.data.setValue({ count: 1, entries: [{ id: 1, name: 'Custom' }], }); initializeShadeRoot({ injector, rootElement, jsxElement: (createComponent("table", null, createComponent(DataGridBody, { service: service, columns: ['id', 'name'], rowComponents: { id: (entry) => createComponent("span", { "data-testid": "custom-id" }, "ID: ", entry.id), name: (entry) => createComponent("strong", { "data-testid": "custom-name" }, entry.name), } }))), }); await flushUpdates(); const customId = document.querySelector('[data-testid="custom-id"]'); const customName = document.querySelector('[data-testid="custom-name"]'); expect(customId?.textContent).toContain('ID: 1'); expect(customName?.textContent).toBe('Custom'); }); }); }); it('should use default row component when column-specific one is not provided', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(new CollectionService(), async (service) => { const rootElement = document.getElementById('root'); service.data.setValue({ count: 1, entries: [{ id: 1, name: 'Default' }], }); initializeShadeRoot({ injector, rootElement, jsxElement: (createComponent("table", null, createComponent(DataGridBody, { service: service, columns: ['id', 'name'], rowComponents: { default: (entry) => createComponent("em", { "data-testid": "default-cell" }, JSON.stringify(entry)), } }))), }); await flushUpdates(); const defaultCells = document.querySelectorAll('[data-testid="default-cell"]'); expect(defaultCells.length).toBe(2); }); }); }); it('should render with empty entries array', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(new CollectionService(), async (service) => { const rootElement = document.getElementById('root'); service.data.setValue({ count: 0, entries: [], }); initializeShadeRoot({ injector, rootElement, jsxElement: (createComponent("table", null, createComponent(DataGridBody, { service: service, columns: ['id', 'name'] }))), }); await flushUpdates(); const body = document.querySelector('tbody[is="shade-data-grid-body"]'); expect(body?.textContent).toContain('- No Data -'); }); }); }); }); //# sourceMappingURL=body.spec.js.map