mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-19 07:54:10 +00:00
Split shared search tips popover owners
This commit is contained in:
parent
035f68e029
commit
326b02842e
7 changed files with 322 additions and 107 deletions
|
|
@ -294,6 +294,14 @@ and `frontend-modern/src/components/shared/searchInputModel.ts` owns the shared
|
|||
search-input contract plus shortcut-hint and trailing-control policy. Future
|
||||
search-input work should extend those owners instead of pushing type-to-search
|
||||
or enhancement wiring back into the shared shell.
|
||||
The shared search tips popover now follows that same owner split.
|
||||
`frontend-modern/src/components/shared/SearchTipsPopover.tsx` stays the render
|
||||
shell, `frontend-modern/src/components/shared/useSearchTipsPopoverState.ts`
|
||||
owns open-state, pointer/focus continuity, and outside-click/Escape listener
|
||||
runtime, and `frontend-modern/src/components/shared/searchTipsPopoverModel.ts`
|
||||
owns trigger variant, label/id defaults, hover policy, and trigger/popover
|
||||
class selection. Future search-tips work should extend those owners instead of
|
||||
pushing listener lifecycle or trigger policy back into the shared shell.
|
||||
The shared tooltip now follows that same owner split.
|
||||
`frontend-modern/src/components/shared/Tooltip.tsx` stays the render shell and
|
||||
singleton API boundary, `frontend-modern/src/components/shared/useTooltipState.ts`
|
||||
|
|
|
|||
|
|
@ -1,111 +1,49 @@
|
|||
import { Component, Show, For, createEffect, createSignal, onCleanup } from 'solid-js';
|
||||
import { Component, Show, For } from 'solid-js';
|
||||
import {
|
||||
getSearchTipsPopoverButtonLabel,
|
||||
getSearchTipsPopoverId,
|
||||
getSearchTipsPopoverPositionClass,
|
||||
getSearchTipsPopoverTitle,
|
||||
getSearchTipsPopoverTriggerClass,
|
||||
getSearchTipsPopoverTriggerVariant,
|
||||
shouldSearchTipsPopoverOpenOnHover,
|
||||
type SearchTipsPopoverProps,
|
||||
} from './searchTipsPopoverModel';
|
||||
import { useSearchTipsPopoverState } from './useSearchTipsPopoverState';
|
||||
|
||||
interface SearchTip {
|
||||
code: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface SearchTipsPopoverProps {
|
||||
buttonLabel?: string;
|
||||
title?: string;
|
||||
intro?: string;
|
||||
tips: SearchTip[];
|
||||
footerText?: string;
|
||||
footerHighlight?: string;
|
||||
popoverId?: string;
|
||||
align?: 'left' | 'right';
|
||||
class?: string;
|
||||
triggerVariant?: 'button' | 'link' | 'icon';
|
||||
openOnHover?: boolean;
|
||||
}
|
||||
export type { SearchTip, SearchTipsPopoverProps } from './searchTipsPopoverModel';
|
||||
|
||||
export const SearchTipsPopover: Component<SearchTipsPopoverProps> = (props) => {
|
||||
const [open, setOpen] = createSignal(false);
|
||||
let popoverRef: HTMLDivElement | undefined;
|
||||
let triggerRef: HTMLButtonElement | undefined;
|
||||
let pointerInside = false;
|
||||
|
||||
const close = () => setOpen(false);
|
||||
|
||||
createEffect(() => {
|
||||
if (!open()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handlePointerDown = (event: PointerEvent) => {
|
||||
const target = event.target as Node;
|
||||
const insidePopover = popoverRef?.contains(target) ?? false;
|
||||
const onTrigger = triggerRef?.contains(target) ?? false;
|
||||
|
||||
if (!insidePopover && !onTrigger) {
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('pointerdown', handlePointerDown);
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
onCleanup(() => {
|
||||
window.removeEventListener('pointerdown', handlePointerDown);
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
});
|
||||
const triggerVariant = () => getSearchTipsPopoverTriggerVariant(props.triggerVariant);
|
||||
const buttonLabel = () => getSearchTipsPopoverButtonLabel(props.buttonLabel);
|
||||
const title = () => getSearchTipsPopoverTitle(props.title);
|
||||
const popoverId = () => getSearchTipsPopoverId(props.popoverId);
|
||||
const positionClass = () => getSearchTipsPopoverPositionClass(props.align);
|
||||
const triggerClass = () => getSearchTipsPopoverTriggerClass(triggerVariant());
|
||||
const openOnHover = () => shouldSearchTipsPopoverOpenOnHover(props.openOnHover);
|
||||
const state = useSearchTipsPopoverState({
|
||||
buttonLabel,
|
||||
openOnHover,
|
||||
});
|
||||
|
||||
const popoverPositionClass = props.align === 'left' ? 'left-0' : 'right-0';
|
||||
const popoverId = props.popoverId ?? 'search-tips-popover';
|
||||
|
||||
const triggerVariant = props.triggerVariant ?? 'button';
|
||||
const openOnHover = props.openOnHover ?? false;
|
||||
const buttonLabel = props.buttonLabel ?? 'Search tips';
|
||||
|
||||
const triggerBaseClasses =
|
||||
'text-xs font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 focus:ring-offset-white dark:focus:ring-blue-400';
|
||||
|
||||
const triggerClasses =
|
||||
triggerVariant === 'button'
|
||||
? `rounded-md border border-border px-2.5 py-1 text-muted transition-colors hover:bg-surface-hover ${triggerBaseClasses}`
|
||||
: triggerVariant === 'link'
|
||||
? `rounded px-1 py-0.5 underline decoration-dotted underline-offset-4 transition-colors hover:text-base-content ${triggerBaseClasses}`
|
||||
: `flex h-5 w-5 items-center justify-center rounded-full transition-colors hover:text-muted ${triggerBaseClasses}`;
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
pointerInside = true;
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
pointerInside = false;
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
class={`relative ${props.class ?? ''}`}
|
||||
onMouseEnter={openOnHover ? handleMouseEnter : undefined}
|
||||
onMouseLeave={openOnHover ? handleMouseLeave : undefined}
|
||||
onMouseEnter={openOnHover() ? state.handleMouseEnter : undefined}
|
||||
onMouseLeave={openOnHover() ? state.handleMouseLeave : undefined}
|
||||
>
|
||||
<button
|
||||
ref={(el) => (triggerRef = el)}
|
||||
ref={state.setTriggerRef}
|
||||
type="button"
|
||||
class={triggerClasses}
|
||||
onClick={openOnHover ? () => setOpen(true) : () => setOpen((value) => !value)}
|
||||
onFocus={() => setOpen(true)}
|
||||
onBlur={() => {
|
||||
if (!pointerInside) {
|
||||
setOpen(false);
|
||||
}
|
||||
}}
|
||||
aria-expanded={open()}
|
||||
aria-controls={popoverId}
|
||||
aria-label={buttonLabel}
|
||||
class={triggerClass()}
|
||||
onClick={state.handleClick}
|
||||
onFocus={state.handleMouseEnter}
|
||||
onBlur={state.handleBlur}
|
||||
aria-expanded={state.isOpen()}
|
||||
aria-controls={popoverId()}
|
||||
aria-label={buttonLabel()}
|
||||
>
|
||||
{triggerVariant === 'icon' ? (
|
||||
{triggerVariant() === 'icon' ? (
|
||||
<>
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path
|
||||
|
|
@ -115,29 +53,27 @@ export const SearchTipsPopover: Component<SearchTipsPopoverProps> = (props) => {
|
|||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">{buttonLabel}</span>
|
||||
<span class="sr-only">{buttonLabel()}</span>
|
||||
</>
|
||||
) : (
|
||||
buttonLabel
|
||||
buttonLabel()
|
||||
)}
|
||||
</button>
|
||||
|
||||
<Show when={open()}>
|
||||
<Show when={state.isOpen()}>
|
||||
<div
|
||||
ref={(el) => (popoverRef = el)}
|
||||
id={popoverId}
|
||||
ref={state.setPopoverRef}
|
||||
id={popoverId()}
|
||||
role="dialog"
|
||||
aria-label={props.title ?? 'Search tips'}
|
||||
class={`absolute ${popoverPositionClass} z-50 mt-2 w-72 overflow-hidden rounded-md border bg-surface text-left shadow-sm`}
|
||||
aria-label={title()}
|
||||
class={`absolute ${positionClass()} z-50 mt-2 w-72 overflow-hidden rounded-md border bg-surface text-left shadow-sm`}
|
||||
>
|
||||
<div class="flex items-center justify-between border-b border-border-subtle px-3 py-2">
|
||||
<span class="text-sm font-semibold text-base-content">
|
||||
{props.title ?? 'Search tips'}
|
||||
</span>
|
||||
<span class="text-sm font-semibold text-base-content">{title()}</span>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded p-1 transition-colors hover:text-muted"
|
||||
onClick={close}
|
||||
onClick={state.close}
|
||||
aria-label="Close search tips"
|
||||
>
|
||||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ import searchFieldSource from '@/components/shared/SearchField.tsx?raw';
|
|||
import searchFieldModelSource from '@/components/shared/searchFieldModel.ts?raw';
|
||||
import searchInputSource from '@/components/shared/SearchInput.tsx?raw';
|
||||
import searchInputModelSource from '@/components/shared/searchInputModel.ts?raw';
|
||||
import searchTipsPopoverSource from '@/components/shared/SearchTipsPopover.tsx?raw';
|
||||
import searchTipsPopoverModelSource from '@/components/shared/searchTipsPopoverModel.ts?raw';
|
||||
import tooltipSource from '@/components/shared/Tooltip.tsx?raw';
|
||||
import tooltipModelSource from '@/components/shared/tooltipModel.ts?raw';
|
||||
import interactiveSparklineSource from '@/components/shared/InteractiveSparkline.tsx?raw';
|
||||
|
|
@ -54,6 +56,7 @@ import infrastructureSelectorStateSource from '@/components/shared/useInfrastruc
|
|||
import pulseDataGridStateSource from '@/components/shared/usePulseDataGridState.ts?raw';
|
||||
import searchFieldStateSource from '@/components/shared/useSearchFieldState.ts?raw';
|
||||
import searchInputStateSource from '@/components/shared/useSearchInputState.ts?raw';
|
||||
import searchTipsPopoverStateSource from '@/components/shared/useSearchTipsPopoverState.ts?raw';
|
||||
import tooltipStateSource from '@/components/shared/useTooltipState.ts?raw';
|
||||
import interactiveSparklineStateSource from '@/components/shared/useInteractiveSparklineState.ts?raw';
|
||||
import webInterfaceUrlFieldSource from '@/components/shared/WebInterfaceUrlField.tsx?raw';
|
||||
|
|
@ -490,6 +493,26 @@ describe('shared primitive guardrails', () => {
|
|||
expect(searchInputModelSource).toContain('export interface SearchInputProps');
|
||||
});
|
||||
|
||||
it('keeps search tips popover on shell, runtime, and model owners', () => {
|
||||
expect(searchTipsPopoverSource).toContain('useSearchTipsPopoverState');
|
||||
expect(searchTipsPopoverSource).toContain('getSearchTipsPopoverTriggerClass');
|
||||
expect(searchTipsPopoverSource).not.toContain('createSignal');
|
||||
expect(searchTipsPopoverSource).not.toContain('createEffect');
|
||||
expect(searchTipsPopoverSource).not.toContain('window.addEventListener');
|
||||
expect(searchTipsPopoverSource).not.toContain('triggerVariant ===');
|
||||
|
||||
expect(searchTipsPopoverStateSource).toContain('export function useSearchTipsPopoverState');
|
||||
expect(searchTipsPopoverStateSource).toContain('createSignal');
|
||||
expect(searchTipsPopoverStateSource).toContain('createEffect');
|
||||
expect(searchTipsPopoverStateSource).toContain('window.addEventListener');
|
||||
expect(searchTipsPopoverStateSource).toContain('pointerInside');
|
||||
|
||||
expect(searchTipsPopoverModelSource).toContain('getSearchTipsPopoverTriggerClass');
|
||||
expect(searchTipsPopoverModelSource).toContain('getSearchTipsPopoverPositionClass');
|
||||
expect(searchTipsPopoverModelSource).toContain('getSearchTipsPopoverTriggerVariant');
|
||||
expect(searchTipsPopoverModelSource).toContain('shouldSearchTipsPopoverOpenOnHover');
|
||||
});
|
||||
|
||||
it('keeps tooltip on shell, runtime, and model owners', () => {
|
||||
expect(tooltipSource).toContain('useTooltipState');
|
||||
expect(tooltipSource).toContain('createTooltipSystemState');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import { cleanup, fireEvent, render, screen } from '@solidjs/testing-library';
|
||||
import { SearchTipsPopover } from '@/components/shared/SearchTipsPopover';
|
||||
import searchTipsPopoverSource from '@/components/shared/SearchTipsPopover.tsx?raw';
|
||||
import searchTipsPopoverModelSource from '@/components/shared/searchTipsPopoverModel.ts?raw';
|
||||
import searchTipsPopoverStateSource from '@/components/shared/useSearchTipsPopoverState.ts?raw';
|
||||
|
||||
describe('SearchTipsPopover', () => {
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('keeps search tips popover on shell, runtime, and model owners', () => {
|
||||
expect(searchTipsPopoverSource).toContain('useSearchTipsPopoverState');
|
||||
expect(searchTipsPopoverSource).toContain('getSearchTipsPopoverTriggerClass');
|
||||
expect(searchTipsPopoverSource).not.toContain('createSignal');
|
||||
expect(searchTipsPopoverSource).not.toContain('createEffect');
|
||||
expect(searchTipsPopoverSource).not.toContain('window.addEventListener');
|
||||
expect(searchTipsPopoverSource).not.toContain('triggerVariant ===');
|
||||
|
||||
expect(searchTipsPopoverStateSource).toContain('export function useSearchTipsPopoverState');
|
||||
expect(searchTipsPopoverStateSource).toContain('createSignal');
|
||||
expect(searchTipsPopoverStateSource).toContain('createEffect');
|
||||
expect(searchTipsPopoverStateSource).toContain('window.addEventListener');
|
||||
expect(searchTipsPopoverStateSource).toContain('pointerInside');
|
||||
|
||||
expect(searchTipsPopoverModelSource).toContain('getSearchTipsPopoverTriggerClass');
|
||||
expect(searchTipsPopoverModelSource).toContain('getSearchTipsPopoverPositionClass');
|
||||
expect(searchTipsPopoverModelSource).toContain('getSearchTipsPopoverTriggerVariant');
|
||||
expect(searchTipsPopoverModelSource).toContain('shouldSearchTipsPopoverOpenOnHover');
|
||||
});
|
||||
|
||||
it('toggles the popover on click by default', async () => {
|
||||
render(() => (
|
||||
<SearchTipsPopover
|
||||
tips={[{ code: 'name:web', description: 'Filter by name' }]}
|
||||
/>
|
||||
));
|
||||
|
||||
const trigger = screen.getByRole('button', { name: 'Search tips' });
|
||||
expect(screen.queryByRole('dialog', { name: 'Search tips' })).toBeNull();
|
||||
|
||||
fireEvent.click(trigger);
|
||||
expect(await screen.findByRole('dialog', { name: 'Search tips' })).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(trigger);
|
||||
expect(screen.queryByRole('dialog', { name: 'Search tips' })).toBeNull();
|
||||
});
|
||||
|
||||
it('opens on hover when configured', async () => {
|
||||
render(() => (
|
||||
<SearchTipsPopover
|
||||
openOnHover
|
||||
tips={[{ code: 'tag:web', description: 'Filter by tag' }]}
|
||||
/>
|
||||
));
|
||||
|
||||
const trigger = screen.getByRole('button', { name: 'Search tips' });
|
||||
fireEvent.mouseEnter(trigger.parentElement as HTMLElement);
|
||||
expect(await screen.findByRole('dialog', { name: 'Search tips' })).toBeInTheDocument();
|
||||
|
||||
fireEvent.mouseLeave(trigger.parentElement as HTMLElement);
|
||||
expect(screen.queryByRole('dialog', { name: 'Search tips' })).toBeNull();
|
||||
});
|
||||
|
||||
it('closes on Escape while open', async () => {
|
||||
render(() => (
|
||||
<SearchTipsPopover
|
||||
tips={[{ code: 'cpu>80', description: 'Filter by CPU threshold' }]}
|
||||
/>
|
||||
));
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Search tips' }));
|
||||
expect(await screen.findByRole('dialog', { name: 'Search tips' })).toBeInTheDocument();
|
||||
|
||||
fireEvent.keyDown(window, { key: 'Escape' });
|
||||
expect(screen.queryByRole('dialog', { name: 'Search tips' })).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
export interface SearchTip {
|
||||
code: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface SearchTipsPopoverProps {
|
||||
buttonLabel?: string;
|
||||
title?: string;
|
||||
intro?: string;
|
||||
tips: SearchTip[];
|
||||
footerText?: string;
|
||||
footerHighlight?: string;
|
||||
popoverId?: string;
|
||||
align?: 'left' | 'right';
|
||||
class?: string;
|
||||
triggerVariant?: 'button' | 'link' | 'icon';
|
||||
openOnHover?: boolean;
|
||||
}
|
||||
|
||||
export function getSearchTipsPopoverPositionClass(align?: 'left' | 'right'): string {
|
||||
return align === 'left' ? 'left-0' : 'right-0';
|
||||
}
|
||||
|
||||
export function getSearchTipsPopoverId(popoverId?: string): string {
|
||||
return popoverId ?? 'search-tips-popover';
|
||||
}
|
||||
|
||||
export function getSearchTipsPopoverButtonLabel(buttonLabel?: string): string {
|
||||
return buttonLabel ?? 'Search tips';
|
||||
}
|
||||
|
||||
export function getSearchTipsPopoverTitle(title?: string): string {
|
||||
return title ?? 'Search tips';
|
||||
}
|
||||
|
||||
export function getSearchTipsPopoverTriggerVariant(
|
||||
triggerVariant?: 'button' | 'link' | 'icon',
|
||||
): 'button' | 'link' | 'icon' {
|
||||
return triggerVariant ?? 'button';
|
||||
}
|
||||
|
||||
export function shouldSearchTipsPopoverOpenOnHover(openOnHover?: boolean): boolean {
|
||||
return openOnHover ?? false;
|
||||
}
|
||||
|
||||
export function getSearchTipsPopoverTriggerClass(
|
||||
triggerVariant: 'button' | 'link' | 'icon',
|
||||
): string {
|
||||
const triggerBaseClasses =
|
||||
'text-xs font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 focus:ring-offset-white dark:focus:ring-blue-400';
|
||||
|
||||
if (triggerVariant === 'button') {
|
||||
return `rounded-md border border-border px-2.5 py-1 text-muted transition-colors hover:bg-surface-hover ${triggerBaseClasses}`;
|
||||
}
|
||||
|
||||
if (triggerVariant === 'link') {
|
||||
return `rounded px-1 py-0.5 underline decoration-dotted underline-offset-4 transition-colors hover:text-base-content ${triggerBaseClasses}`;
|
||||
}
|
||||
|
||||
return `flex h-5 w-5 items-center justify-center rounded-full transition-colors hover:text-muted ${triggerBaseClasses}`;
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
import { createEffect, createSignal, onCleanup } from 'solid-js';
|
||||
import type { Accessor } from 'solid-js';
|
||||
|
||||
interface SearchTipsPopoverState {
|
||||
buttonLabel: Accessor<string>;
|
||||
close: () => void;
|
||||
handleBlur: () => void;
|
||||
handleClick: () => void;
|
||||
handleMouseEnter: () => void;
|
||||
handleMouseLeave: () => void;
|
||||
isOpen: Accessor<boolean>;
|
||||
setPopoverRef: (el: HTMLDivElement) => void;
|
||||
setTriggerRef: (el: HTMLButtonElement) => void;
|
||||
}
|
||||
|
||||
interface SearchTipsPopoverStateOptions {
|
||||
buttonLabel: Accessor<string>;
|
||||
openOnHover: Accessor<boolean>;
|
||||
}
|
||||
|
||||
export function useSearchTipsPopoverState(
|
||||
options: SearchTipsPopoverStateOptions,
|
||||
): SearchTipsPopoverState {
|
||||
const [open, setOpen] = createSignal(false);
|
||||
let popoverRef: HTMLDivElement | undefined;
|
||||
let triggerRef: HTMLButtonElement | undefined;
|
||||
let pointerInside = false;
|
||||
|
||||
const close = () => setOpen(false);
|
||||
|
||||
createEffect(() => {
|
||||
if (!open()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handlePointerDown = (event: PointerEvent) => {
|
||||
const target = event.target as Node;
|
||||
const insidePopover = popoverRef?.contains(target) ?? false;
|
||||
const onTrigger = triggerRef?.contains(target) ?? false;
|
||||
|
||||
if (!insidePopover && !onTrigger) {
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('pointerdown', handlePointerDown);
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
onCleanup(() => {
|
||||
window.removeEventListener('pointerdown', handlePointerDown);
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
buttonLabel: options.buttonLabel,
|
||||
close,
|
||||
handleBlur: () => {
|
||||
if (!pointerInside) {
|
||||
setOpen(false);
|
||||
}
|
||||
},
|
||||
handleClick: () => {
|
||||
if (options.openOnHover()) {
|
||||
setOpen(true);
|
||||
return;
|
||||
}
|
||||
setOpen((value) => !value);
|
||||
},
|
||||
handleMouseEnter: () => {
|
||||
pointerInside = true;
|
||||
setOpen(true);
|
||||
},
|
||||
handleMouseLeave: () => {
|
||||
pointerInside = false;
|
||||
setOpen(false);
|
||||
},
|
||||
isOpen: open,
|
||||
setPopoverRef: (el) => {
|
||||
popoverRef = el;
|
||||
},
|
||||
setTriggerRef: (el) => {
|
||||
triggerRef = el;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -34,6 +34,8 @@ import searchFieldSource from '@/components/shared/SearchField.tsx?raw';
|
|||
import searchFieldModelSource from '@/components/shared/searchFieldModel.ts?raw';
|
||||
import searchInputSource from '@/components/shared/SearchInput.tsx?raw';
|
||||
import searchInputModelSource from '@/components/shared/searchInputModel.ts?raw';
|
||||
import searchTipsPopoverSource from '@/components/shared/SearchTipsPopover.tsx?raw';
|
||||
import searchTipsPopoverModelSource from '@/components/shared/searchTipsPopoverModel.ts?raw';
|
||||
import tooltipSource from '@/components/shared/Tooltip.tsx?raw';
|
||||
import tooltipModelSource from '@/components/shared/tooltipModel.ts?raw';
|
||||
import infrastructureSummaryTableSource from '@/components/shared/InfrastructureSummaryTable.tsx?raw';
|
||||
|
|
@ -53,6 +55,7 @@ import infrastructureSelectorStateSource from '@/components/shared/useInfrastruc
|
|||
import pulseDataGridStateSource from '@/components/shared/usePulseDataGridState.ts?raw';
|
||||
import searchFieldStateSource from '@/components/shared/useSearchFieldState.ts?raw';
|
||||
import searchInputStateSource from '@/components/shared/useSearchInputState.ts?raw';
|
||||
import searchTipsPopoverStateSource from '@/components/shared/useSearchTipsPopoverState.ts?raw';
|
||||
import tooltipStateSource from '@/components/shared/useTooltipState.ts?raw';
|
||||
import interactiveSparklineStateSource from '@/components/shared/useInteractiveSparklineState.ts?raw';
|
||||
import infrastructureSummaryTableStateSource from '@/components/shared/useInfrastructureSummaryTableState.ts?raw';
|
||||
|
|
@ -2690,6 +2693,19 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(searchInputStateSource).toContain('getSearchInputShortcutHint');
|
||||
expect(searchInputModelSource).toContain('getSearchInputShortcutHint');
|
||||
expect(searchInputModelSource).toContain('shouldSearchInputShowTrailingControls');
|
||||
expect(searchTipsPopoverSource).toContain('useSearchTipsPopoverState');
|
||||
expect(searchTipsPopoverSource).toContain('getSearchTipsPopoverTriggerClass');
|
||||
expect(searchTipsPopoverSource).not.toContain('createSignal');
|
||||
expect(searchTipsPopoverSource).not.toContain('createEffect');
|
||||
expect(searchTipsPopoverSource).not.toContain('window.addEventListener');
|
||||
expect(searchTipsPopoverStateSource).toContain('createSignal');
|
||||
expect(searchTipsPopoverStateSource).toContain('createEffect');
|
||||
expect(searchTipsPopoverStateSource).toContain('window.addEventListener');
|
||||
expect(searchTipsPopoverStateSource).toContain('pointerInside');
|
||||
expect(searchTipsPopoverModelSource).toContain('getSearchTipsPopoverTriggerClass');
|
||||
expect(searchTipsPopoverModelSource).toContain('getSearchTipsPopoverPositionClass');
|
||||
expect(searchTipsPopoverModelSource).toContain('getSearchTipsPopoverTriggerVariant');
|
||||
expect(searchTipsPopoverModelSource).toContain('shouldSearchTipsPopoverOpenOnHover');
|
||||
expect(tooltipSource).toContain('useTooltipState');
|
||||
expect(tooltipSource).toContain('createTooltipSystemState');
|
||||
expect(tooltipSource).not.toContain('createSignal');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue