UNPKG

@furystack/shades-common-components

Version:

Common UI components for FuryStack Shades

394 lines 19.9 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 { CommandPaletteManager } from './command-palette-manager.js'; import { CommandPaletteSuggestionList } from './command-palette-suggestion-list.js'; describe('CommandPaletteSuggestionList', () => { let originalAnimate; let animateCalls; beforeEach(() => { document.body.innerHTML = '<div id="root" style="width: 500px;"></div>'; animateCalls = []; originalAnimate = Element.prototype.animate; Element.prototype.animate = vi.fn((keyframes, options) => { animateCalls.push({ 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(), }; setTimeout(() => { mockAnimation.onfinish?.({}); }, 10); return mockAnimation; }); }); afterEach(() => { document.body.innerHTML = ''; Element.prototype.animate = originalAnimate; vi.restoreAllMocks(); }); const createManager = () => { return new CommandPaletteManager([]); }; const createSuggestion = (text, score) => ({ element: createComponent("span", null, text), score, onSelected: vi.fn(), }); it('should render as custom element', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); const component = document.querySelector('shade-command-palette-suggestion-list'); expect(component).not.toBeNull(); }); }); }); it('should render suggestion items container', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); const component = document.querySelector('shade-command-palette-suggestion-list'); const container = component?.querySelector('.suggestion-items-container'); expect(container).not.toBeNull(); }); }); }); it('should render suggestions from manager', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { manager.currentSuggestions.setValue([createSuggestion('Command 1', 100), createSuggestion('Command 2', 90)]); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); const component = document.querySelector('shade-command-palette-suggestion-list'); const items = component?.querySelectorAll('.suggestion-item'); expect(items?.length).toBe(2); }); }); }); it('should render suggestion content', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { manager.currentSuggestions.setValue([createSuggestion('Test Command', 100)]); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); const component = document.querySelector('shade-command-palette-suggestion-list'); const item = component?.querySelector('.suggestion-item'); expect(item?.textContent).toContain('Test Command'); }); }); }); it('should mark first item as selected by default', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { manager.currentSuggestions.setValue([createSuggestion('Command 1', 100), createSuggestion('Command 2', 90)]); manager.selectedIndex.setValue(0); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); const component = document.querySelector('shade-command-palette-suggestion-list'); const items = component?.querySelectorAll('.suggestion-item'); expect(items?.[0]?.classList.contains('selected')).toBe(true); expect(items?.[1]?.classList.contains('selected')).toBe(false); }); }); }); it('should update selected class when selectedIndex changes', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { manager.currentSuggestions.setValue([createSuggestion('Command 1', 100), createSuggestion('Command 2', 90)]); manager.selectedIndex.setValue(0); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); manager.selectedIndex.setValue(1); await flushUpdates(); const component = document.querySelector('shade-command-palette-suggestion-list'); const items = component?.querySelectorAll('.suggestion-item'); expect(items?.[0]?.classList.contains('selected')).toBe(false); expect(items?.[1]?.classList.contains('selected')).toBe(true); }); }); }); it('should call selectSuggestion when item is clicked while opened', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { const suggestion = createSuggestion('Click Me', 100); manager.currentSuggestions.setValue([suggestion]); manager.isOpened.setValue(true); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); const selectSpy = vi.spyOn(manager, 'selectSuggestion'); const component = document.querySelector('shade-command-palette-suggestion-list'); const item = component?.querySelector('.suggestion-item'); item?.click(); expect(selectSpy).toHaveBeenCalledWith(injector, 0); }); }); }); it('should not call selectSuggestion when item is clicked while closed', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { const suggestion = createSuggestion('Click Me', 100); manager.currentSuggestions.setValue([suggestion]); manager.isOpened.setValue(false); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); const selectSpy = vi.spyOn(manager, 'selectSuggestion'); const component = document.querySelector('shade-command-palette-suggestion-list'); const item = component?.querySelector('.suggestion-item'); item?.click(); expect(selectSpy).not.toHaveBeenCalled(); }); }); }); it('should animate slide-in when opening', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { manager.isOpened.setValue(false); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); animateCalls = []; manager.isOpened.setValue(true); await flushUpdates(); const slideAnimation = animateCalls.find((call) => Array.isArray(call.keyframes) && call.keyframes.some((kf) => kf.transform === 'translate(0, -50px)') && call.keyframes.some((kf) => kf.transform === 'translate(0, 0)')); expect(slideAnimation).toBeDefined(); expect(slideAnimation?.options?.duration).toBe(500); expect(slideAnimation?.options?.fill).toBe('forwards'); }); }); }); it('should animate slide-out when closing', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { manager.isOpened.setValue(true); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); animateCalls = []; manager.isOpened.setValue(false); await flushUpdates(); const slideAnimation = animateCalls.find((call) => Array.isArray(call.keyframes) && call.keyframes.some((kf) => kf.transform === 'translate(0, 0)') && call.keyframes.some((kf) => kf.transform === 'translate(0, -50px)')); expect(slideAnimation).toBeDefined(); expect(slideAnimation?.options?.duration).toBe(200); }); }); }); it('should set container display to initial when opening', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { manager.isOpened.setValue(false); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); manager.isOpened.setValue(true); await flushUpdates(); const component = document.querySelector('shade-command-palette-suggestion-list'); const container = component?.querySelector('.suggestion-items-container'); expect(container?.style.display).toBe('initial'); }); }); }); it('should set container display to none when closing', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { manager.isOpened.setValue(true); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); manager.isOpened.setValue(false); await flushUpdates(); const component = document.querySelector('shade-command-palette-suggestion-list'); const container = component?.querySelector('.suggestion-items-container'); expect(container?.style.display).toBe('none'); }); }); }); it('should render empty list when no suggestions', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { manager.currentSuggestions.setValue([]); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); const component = document.querySelector('shade-command-palette-suggestion-list'); const items = component?.querySelectorAll('.suggestion-item'); expect(items?.length).toBe(0); }); }); }); it('should support fullScreenSuggestions prop', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { manager.isOpened.setValue(true); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager, fullScreenSuggestions: true }), }); await flushUpdates(); const component = document.querySelector('shade-command-palette-suggestion-list'); const container = component?.querySelector('.suggestion-items-container'); expect(container?.style.left).toBe('0px'); expect(container?.style.width).toBe('calc(100% - 42px)'); }); }); }); it('should set max height based on window height', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { manager.isOpened.setValue(true); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); const component = document.querySelector('shade-command-palette-suggestion-list'); const container = component?.querySelector('.suggestion-items-container'); const expectedMaxHeight = `${window.innerHeight * 0.8}px`; expect(container?.style.maxHeight).toBe(expectedMaxHeight); }); }); }); it('should have correct CSS styles for suggestion items container', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); const component = document.querySelector('shade-command-palette-suggestion-list'); const container = component?.querySelector('.suggestion-items-container'); const computedStyle = window.getComputedStyle(container); expect(computedStyle.position).toBe('absolute'); expect(computedStyle.overflow).toBe('hidden'); expect(computedStyle.zIndex).toBe('1'); }); }); }); it('should call selectSuggestion with correct index for second item', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { manager.currentSuggestions.setValue([ createSuggestion('First', 100), createSuggestion('Second', 90), createSuggestion('Third', 80), ]); manager.isOpened.setValue(true); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); const selectSpy = vi.spyOn(manager, 'selectSuggestion'); const component = document.querySelector('shade-command-palette-suggestion-list'); const items = component?.querySelectorAll('.suggestion-item'); items[1]?.click(); expect(selectSpy).toHaveBeenCalledWith(injector, 1); }); }); }); it('should update container z-index when opening and closing', async () => { await usingAsync(createInjector(), async (injector) => { await usingAsync(createManager(), async (manager) => { manager.isOpened.setValue(false); const rootElement = document.getElementById('root'); initializeShadeRoot({ injector, rootElement, jsxElement: createComponent(CommandPaletteSuggestionList, { manager: manager }), }); await flushUpdates(); manager.isOpened.setValue(true); await flushUpdates(); const component = document.querySelector('shade-command-palette-suggestion-list'); const container = component?.querySelector('.suggestion-items-container'); expect(container?.style.zIndex).toBe('1'); manager.isOpened.setValue(false); await flushUpdates(); expect(container?.style.zIndex).toBe('-1'); }); }); }); }); //# sourceMappingURL=command-palette-suggestion-list.spec.js.map