@furystack/shades-common-components
Version:
Common UI components for FuryStack Shades
644 lines • 35.8 kB
JavaScript
import { createInjector } from '@furystack/inject';
import { createComponent, initializeShadeRoot } from '@furystack/shades';
import { usingAsync } from '@furystack/utils';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { Suggest } from './index.js';
describe('Suggest', () => {
let originalAnimate;
let animateCalls;
beforeEach(() => {
vi.useFakeTimers();
document.body.innerHTML = '<div id="root"></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(),
};
return mockAnimation;
});
});
afterEach(async () => {
await vi.runAllTimersAsync();
const suggest = document.querySelector('shade-suggest');
suggest?.remove();
document.body.innerHTML = '';
Element.prototype.animate = originalAnimate;
vi.useRealTimers();
vi.restoreAllMocks();
});
const advanceTimers = async (ms) => {
await vi.advanceTimersByTimeAsync(ms);
};
const createTestEntries = () => [
{ id: 1, name: 'First' },
{ id: 2, name: 'Second' },
{ id: 3, name: 'Third' },
];
const getTestEntries = async (term) => {
const entries = createTestEntries();
if (!term)
return entries;
return entries.filter((e) => e.name.toLowerCase().includes(term.toLowerCase()));
};
const getSuggestionEntry = (entry) => ({
element: createComponent("span", { "data-testid": `suggestion-${entry.id}` }, entry.name),
score: entry.id,
});
describe('rendering', () => {
it('should render as custom element', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
expect(suggest).not.toBeNull();
});
});
it('should render the default prefix', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "Search:", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const termIcon = suggest?.querySelector('.term-icon');
expect(termIcon?.textContent).toBe('Search:');
});
});
it('should render the input container', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const inputContainer = suggest?.querySelector('.input-container');
expect(inputContainer).not.toBeNull();
});
});
it('should apply custom styles', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion, style: { backgroundColor: 'red' } })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const inputContainer = suggest?.querySelector('.input-container');
expect(inputContainer?.style.backgroundColor).toBe('red');
});
});
});
describe('keyboard navigation', () => {
it('should handle ArrowDown to move selection down', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const wrapper = suggest?.querySelector('.suggest-wrapper');
const input = suggest?.querySelector('input');
input.value = 'test';
input.dispatchEvent(new Event('input', { bubbles: true }));
await advanceTimers(300);
const arrowDownEvent = new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true });
Object.defineProperty(arrowDownEvent, 'target', { value: input });
wrapper?.dispatchEvent(arrowDownEvent);
await advanceTimers(50);
const selectedItems = suggest?.querySelectorAll('.suggestion-item.selected');
expect(selectedItems?.length).toBeGreaterThanOrEqual(0);
});
});
it('should handle ArrowUp to move selection up', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const wrapper = suggest?.querySelector('.suggest-wrapper');
const input = suggest?.querySelector('input');
input.value = 'test';
input.dispatchEvent(new Event('input', { bubbles: true }));
await advanceTimers(300);
const arrowDownEvent = new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true });
Object.defineProperty(arrowDownEvent, 'target', { value: input });
wrapper?.dispatchEvent(arrowDownEvent);
wrapper?.dispatchEvent(arrowDownEvent);
const arrowUpEvent = new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true });
Object.defineProperty(arrowUpEvent, 'target', { value: input });
wrapper?.dispatchEvent(arrowUpEvent);
await advanceTimers(50);
expect(suggest).not.toBeNull();
});
});
it('should handle Enter to select current suggestion', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const wrapper = suggest?.querySelector('.suggest-wrapper');
const input = suggest?.querySelector('input');
input.value = 'First';
input.dispatchEvent(new Event('input', { bubbles: true }));
await advanceTimers(300);
const enterEvent = new KeyboardEvent('keydown', { key: 'Enter', bubbles: true });
Object.defineProperty(enterEvent, 'target', { value: input });
wrapper?.dispatchEvent(enterEvent);
await advanceTimers(50);
expect(onSelectSuggestion).toHaveBeenCalled();
});
});
it('should prevent default on Enter key', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const wrapper = suggest?.querySelector('.suggest-wrapper');
const input = suggest?.querySelector('input');
input.value = 'First';
input.dispatchEvent(new Event('input', { bubbles: true }));
await advanceTimers(300);
const enterEvent = new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, cancelable: true });
Object.defineProperty(enterEvent, 'target', { value: input });
const preventDefaultSpy = vi.spyOn(enterEvent, 'preventDefault');
wrapper?.dispatchEvent(enterEvent);
expect(preventDefaultSpy).toHaveBeenCalled();
});
});
it('should prevent default on ArrowUp key when suggestions are open', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const wrapper = suggest?.querySelector('.suggest-wrapper');
const input = suggest?.querySelector('input');
input.value = 'First';
input.dispatchEvent(new Event('input', { bubbles: true }));
await advanceTimers(300);
const arrowUpEvent = new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true, cancelable: true });
Object.defineProperty(arrowUpEvent, 'target', { value: input });
const preventDefaultSpy = vi.spyOn(arrowUpEvent, 'preventDefault');
wrapper?.dispatchEvent(arrowUpEvent);
expect(preventDefaultSpy).toHaveBeenCalled();
});
});
it('should prevent default on ArrowDown key when suggestions are open', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const wrapper = suggest?.querySelector('.suggest-wrapper');
const input = suggest?.querySelector('input');
input.value = 'First';
input.dispatchEvent(new Event('input', { bubbles: true }));
await advanceTimers(300);
const arrowDownEvent = new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true, cancelable: true });
Object.defineProperty(arrowDownEvent, 'target', { value: input });
const preventDefaultSpy = vi.spyOn(arrowDownEvent, 'preventDefault');
wrapper?.dispatchEvent(arrowDownEvent);
expect(preventDefaultSpy).toHaveBeenCalled();
});
});
it('should not prevent default on arrow keys when dropdown is closed', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const wrapper = suggest?.querySelector('.suggest-wrapper');
const input = suggest?.querySelector('input');
const arrowDownEvent = new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true, cancelable: true });
Object.defineProperty(arrowDownEvent, 'target', { value: input });
const preventDefaultSpy = vi.spyOn(arrowDownEvent, 'preventDefault');
wrapper?.dispatchEvent(arrowDownEvent);
expect(preventDefaultSpy).not.toHaveBeenCalled();
});
});
it('should not move selection below 0', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const wrapper = suggest?.querySelector('.suggest-wrapper');
const input = suggest?.querySelector('input');
input.value = 'test';
input.dispatchEvent(new Event('input', { bubbles: true }));
await advanceTimers(300);
for (let i = 0; i < 5; i++) {
const arrowUpEvent = new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true });
Object.defineProperty(arrowUpEvent, 'target', { value: input });
wrapper?.dispatchEvent(arrowUpEvent);
}
await advanceTimers(50);
expect(suggest).not.toBeNull();
});
});
});
describe('open/close behavior', () => {
it('should open when clicking term icon', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const termIcon = suggest?.querySelector('.term-icon');
termIcon?.click();
await advanceTimers(50);
expect(suggest?.hasAttribute('data-opened')).toBe(true);
});
});
it('should close when clicking close button', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const termIcon = suggest?.querySelector('.term-icon');
termIcon?.click();
await advanceTimers(50);
const closeButton = suggest?.querySelector('.close-suggestions');
closeButton?.click();
await advanceTimers(50);
expect(suggest?.hasAttribute('data-opened')).toBe(false);
});
});
it('should trigger animation when opening', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const initialAnimationCount = animateCalls.length;
const suggest = document.querySelector('shade-suggest');
const termIcon = suggest?.querySelector('.term-icon');
termIcon?.click();
await advanceTimers(50);
expect(animateCalls.length).toBeGreaterThan(initialAnimationCount);
});
});
it('should trigger animation when closing', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const termIcon = suggest?.querySelector('.term-icon');
termIcon?.click();
await advanceTimers(50);
const animationCountAfterOpen = animateCalls.length;
const closeButton = suggest?.querySelector('.close-suggestions');
closeButton?.click();
await advanceTimers(50);
expect(animateCalls.length).toBeGreaterThan(animationCountAfterOpen);
});
});
});
describe('suggestions loading', () => {
it('should fetch suggestions when typing', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
const getEntriesSpy = vi.fn(getTestEntries);
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getEntriesSpy, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const input = suggest?.querySelector('input');
input.value = 'First';
input.dispatchEvent(new Event('input', { bubbles: true }));
await advanceTimers(300);
expect(getEntriesSpy).toHaveBeenCalledWith('First');
});
});
it('should show loader animation while loading', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
const resolveHolder = { resolve: null };
const slowGetEntries = () => new Promise((resolve) => {
resolveHolder.resolve = resolve;
});
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: slowGetEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const input = suggest?.querySelector('input');
input.value = 'test';
input.dispatchEvent(new Event('input', { bubbles: true }));
await advanceTimers(300);
const loader = suggest?.querySelector('shade-loader');
expect(loader).not.toBeNull();
resolveHolder.resolve?.(createTestEntries());
await advanceTimers(50);
});
});
it('should render suggestions after loading', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const input = suggest?.querySelector('input');
input.value = 'test';
input.dispatchEvent(new Event('input', { bubbles: true }));
await advanceTimers(300);
const suggestionList = suggest?.querySelector('shade-suggest-suggestion-list');
expect(suggestionList).not.toBeNull();
});
});
});
describe('suggestion selection', () => {
it('should call onSelectSuggestion when selecting via Enter', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const wrapper = suggest?.querySelector('.suggest-wrapper');
const input = suggest?.querySelector('input');
input.value = 'First';
input.dispatchEvent(new Event('input', { bubbles: true }));
await advanceTimers(300);
const enterEvent = new KeyboardEvent('keydown', { key: 'Enter', bubbles: true });
Object.defineProperty(enterEvent, 'target', { value: input });
wrapper?.dispatchEvent(enterEvent);
await advanceTimers(50);
expect(onSelectSuggestion).toHaveBeenCalledWith(expect.objectContaining({ name: 'First' }));
});
});
it('should close after selecting a suggestion', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const wrapper = suggest?.querySelector('.suggest-wrapper');
const input = suggest?.querySelector('input');
input.value = 'First';
input.dispatchEvent(new Event('input', { bubbles: true }));
await advanceTimers(300);
expect(suggest?.hasAttribute('data-opened')).toBe(true);
const enterEvent = new KeyboardEvent('keydown', { key: 'Enter', bubbles: true });
Object.defineProperty(enterEvent, 'target', { value: input });
wrapper?.dispatchEvent(enterEvent);
await advanceTimers(50);
expect(suggest?.hasAttribute('data-opened')).toBe(false);
});
});
});
describe('sub-components', () => {
it('should render SuggestInput component', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const suggestInput = suggest?.querySelector('shades-suggest-input');
expect(suggestInput).not.toBeNull();
});
});
it('should render SuggestionList component', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const suggestionList = suggest?.querySelector('shade-suggest-suggestion-list');
expect(suggestionList).not.toBeNull();
});
});
it('should render Loader component', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const loader = suggest?.querySelector('shade-loader');
expect(loader).not.toBeNull();
});
});
});
describe('spatial navigation attributes', () => {
it('should have data-spatial-nav-target on the host element', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
expect(suggest.hasAttribute('data-spatial-nav-target')).toBe(true);
});
});
it('should have tabIndex of -1 on the host element', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
expect(suggest.tabIndex).toBe(-1);
});
});
it('should delegate focus to the inner input when the host is focused', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", getEntries: getTestEntries, getSuggestionEntry: getSuggestionEntry, onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const input = suggest.querySelector('input');
suggest.dispatchEvent(new FocusEvent('focus', { bubbles: false }));
await advanceTimers(10);
expect(document.activeElement).toBe(input);
});
});
});
describe('synchronous suggestions mode', () => {
it('should render with string[] suggestions', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", suggestions: ['Apple', 'Banana', 'Cherry'], onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
expect(suggest).not.toBeNull();
});
});
it('should render input in sync mode', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", suggestions: ['Apple', 'Banana', 'Cherry'], onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const input = suggest?.querySelector('input');
expect(input).not.toBeNull();
});
});
it('should show filtered suggestions in sync mode', async () => {
await usingAsync(createInjector(), async (injector) => {
const rootElement = document.getElementById('root');
const onSelectSuggestion = vi.fn();
initializeShadeRoot({
injector,
rootElement,
jsxElement: (createComponent(Suggest, { defaultPrefix: "\uD83D\uDD0D", suggestions: ['Apple', 'Apricot', 'Banana'], onSelectSuggestion: onSelectSuggestion })),
});
await advanceTimers(50);
const suggest = document.querySelector('shade-suggest');
const input = suggest?.querySelector('input');
input.value = 'ap';
input.dispatchEvent(new Event('input', { bubbles: true }));
await advanceTimers(300);
const suggestionItems = suggest?.querySelectorAll('.suggestion-item');
expect(suggestionItems?.length).toBe(2);
});
});
});
});
//# sourceMappingURL=index.spec.js.map