FN-8193: add collapsible sticky provider lists

Keep model-provider headers visible and let users collapse provider model rows with persisted local preferences.

- Add sticky provider headers and accessible collapse toggles to CustomModelDropdown
- Persist collapsed provider groups locally while keeping filtered matches visible
- Cover collapse, keyboard navigation, storage recovery, and empty-state behavior
- Document the dashboard behavior and add a minor changeset

Files changed:
 .changeset/fn-8193-collapsible-provider-lists.md   |   7 ++
 docs/dashboard-guide.md                            |   1 +
 .../app/components/CustomModelDropdown.css         |  48 +++++++-
 .../app/components/CustomModelDropdown.tsx         | 110 ++++++++++++++----
 .../__tests__/CustomModelDropdown.test.tsx         | 129 +++++++++++++++++++++
 5 files changed, 266 insertions(+), 29 deletions(-)

Fusion-Task-Id: FN-8193

Fusion-Task-Lineage: 4fbc43a8-ef81-463c-b4d0-5f00a57ab27b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-17 00:48:36 -07:00
parent c3e98d193f
commit 3fdc2c1fb7
5 changed files with 266 additions and 29 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": minor
---
summary: Model dropdowns keep the provider header pinned while scrolling and let you collapse each provider list.
category: feature
dev: CustomModelDropdown gains CSS sticky provider headers and a per-provider collapse chevron persisted to localStorage (key fusion-dashboard-model-dropdown-collapsed-providers).

View File

@@ -532,6 +532,7 @@ Rules:
- `Merge target / base branch` stays optional for all modes and uses the same branch-dropdown + `Custom…` fallback behavior as Planning Mode. - `Merge target / base branch` stays optional for all modes and uses the same branch-dropdown + `Custom…` fallback behavior as Planning Mode.
- In **More options → Model Configuration**, **Auto-merge** is a per-task override with three states: **Default** (follow project setting), **Enabled**, or **Disabled**. - In **More options → Model Configuration**, **Auto-merge** is a per-task override with three states: **Default** (follow project setting), **Enabled**, or **Disabled**.
- In **More options → Model Configuration**, task and agent model pickers expose **Thinking Level** inside the same model dropdown panel instead of as a separate adjacent selector. Task pickers offer **Default (project setting)** plus **Off**, **Minimal**, **Low**, **Medium**, **High**, and **Very High**; agent creation, Agent Onboarding review, and Agent Detail built-in-model settings are concrete-only and start/fall back to **Off**. - In **More options → Model Configuration**, task and agent model pickers expose **Thinking Level** inside the same model dropdown panel instead of as a separate adjacent selector. Task pickers offer **Default (project setting)** plus **Off**, **Minimal**, **Low**, **Medium**, **High**, and **Very High**; agent creation, Agent Onboarding review, and Agent Detail built-in-model settings are concrete-only and start/fall back to **Off**.
- Shared model dropdowns keep the active provider header visible while scrolling. Use the provider chevron to collapse a provider's model rows; this dashboard-local preference persists across sessions, while filtering temporarily shows matching rows from collapsed providers.
- In **More options → Model Configuration**, **Planner oversight** is a per-task override of the workflow-native `plannerOversightLevel` setting (FN-7508): **Inherit from workflow** (default) plus **Off**, **Observe**, **Steer**, and **Autonomous recovery**. This selector appears in both the New Task dialog and the Task Detail edit form (same shared control). Selecting **Inherit from workflow** clears the per-task override (sent as `null` on edit, omitted on create) so the task falls back to the effective `plannerOversightLevel` configured on its workflow — set project/global defaults for this in the **Workflow Editor → Values** tab, not in Project Settings; it is workflow-native, not a project setting. - In **More options → Model Configuration**, **Planner oversight** is a per-task override of the workflow-native `plannerOversightLevel` setting (FN-7508): **Inherit from workflow** (default) plus **Off**, **Observe**, **Steer**, and **Autonomous recovery**. This selector appears in both the New Task dialog and the Task Detail edit form (same shared control). Selecting **Inherit from workflow** clears the per-task override (sent as `null` on edit, omitted on create) so the task falls back to the effective `plannerOversightLevel` configured on its workflow — set project/global defaults for this in the **Workflow Editor → Values** tab, not in Project Settings; it is workflow-native, not a project setting.
The dialog also exposes AI handoffs that quick-add no longer shows: **Plan** opens Planning Mode with the current description, and **Subtask** opens Subtask Breakdown with the current description when **Settings → Experimental Features → Subtask Breakdown** is enabled. The Subtask handoff is hidden by default; visible handoff buttons remain disabled until the description has content, matching the quick-add row behavior for Subtask. **Execution mode** and optional workflow-step selection are available in the New Task dialog as well as quick entry, so users can choose Fast or standard execution and opt into workflow-specific creation-time steps before creating a task from either surface. The dialog also exposes AI handoffs that quick-add no longer shows: **Plan** opens Planning Mode with the current description, and **Subtask** opens Subtask Breakdown with the current description when **Settings → Experimental Features → Subtask Breakdown** is enabled. The Subtask handoff is hidden by default; visible handoff buttons remain disabled until the description has content, matching the quick-add row behavior for Subtask. **Execution mode** and optional workflow-step selection are available in the New Task dialog as well as quick entry, so users can choose Fast or standard execution and opt into workflow-specific creation-time steps before creating a task from either surface.

View File

@@ -310,15 +310,22 @@ FN-7760 requires the portaled model list to remain the touch-scroll owner on mob
border-bottom: none; border-bottom: none;
} }
/*
FNXC:ModelDropdown 2026-07-15-00:00:
Provider headers must remain visible while their long model groups scroll in the shared list. Sticky positioning is scoped to the list scroller, so it cannot overlap the search, result-count, or thinking controls above it.
*/
.model-combobox-optgroup { .model-combobox-optgroup {
position: sticky;
top: 0;
z-index: 1;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: var(--space-sm);
padding: 6px 12px; padding: var(--space-sm) var(--space-md);
font-size: 10px; font-size: var(--font-size-xs);
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.5px; letter-spacing: 0.05em;
color: var(--text-muted); color: var(--text-muted);
background: var(--bg); background: var(--bg);
cursor: default; cursor: default;
@@ -350,6 +357,34 @@ FN-7760 requires the portaled model list to remain the touch-scroll owner on mob
color: var(--star-active); color: var(--star-active);
} }
.model-combobox-optgroup-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
padding: var(--space-xs);
color: var(--text-muted);
background: transparent;
border: 0;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: var(--font-size-sm);
line-height: 1;
flex-shrink: 0;
transition: color var(--transition-fast), transform var(--transition-fast), background var(--transition-fast);
transform: rotate(-90deg);
}
.model-combobox-optgroup-toggle:hover,
.model-combobox-optgroup-toggle:focus-visible {
color: var(--text);
background: var(--card-hover);
outline: none;
}
.model-combobox-optgroup-toggle--expanded {
transform: rotate(0);
}
/* Model favorite star (inside option row) */ /* Model favorite star (inside option row) */
.model-combobox-option-favorite { .model-combobox-option-favorite {
padding: 2px 4px; padding: 2px 4px;
@@ -401,8 +436,9 @@ FN-7760 requires the portaled model list to remain the touch-scroll owner on mob
font-size: 16px; font-size: 16px;
} }
.model-combobox-option { .model-combobox-option,
min-height: 36px; .model-combobox-optgroup-toggle {
min-height: calc(var(--space-lg) * 2 + var(--space-xs));
} }
} }

View File

@@ -48,6 +48,25 @@ interface DropdownPosition {
maxHeight: number; maxHeight: number;
} }
const COLLAPSED_PROVIDERS_STORAGE_KEY = "fusion-dashboard-model-dropdown-collapsed-providers";
/**
* FNXC:ModelDropdown 2026-07-15-00:00:
* Provider-group collapse is dashboard-local preference state, not a server setting. Read defensively so SSR, unavailable storage, and malformed legacy data keep every provider expanded instead of breaking a model picker.
*/
function loadCollapsedProviders(): Set<string> {
if (typeof window === "undefined") return new Set();
try {
const raw = window.localStorage.getItem(COLLAPSED_PROVIDERS_STORAGE_KEY);
if (!raw) return new Set();
const parsed: unknown = JSON.parse(raw);
return Array.isArray(parsed) ? new Set(parsed.filter((provider): provider is string => typeof provider === "string")) : new Set();
} catch {
return new Set();
}
}
/** /**
* CustomModelDropdown - A dropdown component combining selection with icon-enhanced provider groups. * CustomModelDropdown - A dropdown component combining selection with icon-enhanced provider groups.
* *
@@ -91,6 +110,7 @@ export function CustomModelDropdown({
const [highlightedIndex, setHighlightedIndex] = useState(0); const [highlightedIndex, setHighlightedIndex] = useState(0);
const [dropdownPosition, setDropdownPosition] = useState<DropdownPosition | null>(null); const [dropdownPosition, setDropdownPosition] = useState<DropdownPosition | null>(null);
const [portalRoot, setPortalRoot] = useState<HTMLElement | null>(null); const [portalRoot, setPortalRoot] = useState<HTMLElement | null>(null);
const [collapsedProviders, setCollapsedProviders] = useState<Set<string>>(loadCollapsedProviders);
const generatedThinkingId = useId(); const generatedThinkingId = useId();
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
@@ -101,6 +121,7 @@ export function CustomModelDropdown({
// Filter models based on local filter text // Filter models based on local filter text
const filteredModels = useMemo(() => filterModels(models, localFilter), [models, localFilter]); const filteredModels = useMemo(() => filterModels(models, localFilter), [models, localFilter]);
const hasFilter = localFilter.length > 0;
// Group filtered models by provider and sort by favorites // Group filtered models by provider and sort by favorites
const modelsByProvider = useMemo(() => { const modelsByProvider = useMemo(() => {
@@ -150,6 +171,21 @@ export function CustomModelDropdown({
}); });
}, [modelsByProvider, favoriteProviders]); }, [modelsByProvider, favoriteProviders]);
/*
FNXC:ModelDropdown 2026-07-15-00:00:
Collapsed provider rows are omitted from both the DOM and keyboard option list. An active filter temporarily expands every matching group so search never hides a matching model; the saved collapsed preference resumes after clearing the filter.
*/
const visibleProviderEntries = useMemo(() => sortedProviderEntries.flatMap(([provider, providerModels]) => {
const nonFavoritedModels = providerModels.filter((model) => !favoriteModels.includes(`${model.provider}/${model.id}`));
if (nonFavoritedModels.length === 0) return [];
return [{
provider,
models: nonFavoritedModels,
isCollapsed: !hasFilter && collapsedProviders.has(provider),
}];
}), [collapsedProviders, favoriteModels, hasFilter, sortedProviderEntries]);
const hasNoChangeOption = typeof noChangeValue === "string" && noChangeValue.length > 0; const hasNoChangeOption = typeof noChangeValue === "string" && noChangeValue.length > 0;
const shouldShowThinking = showThinkingLevel ?? Boolean(onThinkingLevelChange); const shouldShowThinking = showThinkingLevel ?? Boolean(onThinkingLevelChange);
const normalizedThinkingLevel = thinkingLevel ?? ""; const normalizedThinkingLevel = thinkingLevel ?? "";
@@ -208,20 +244,21 @@ export function CustomModelDropdown({
}); });
} }
sortedProviderEntries.forEach(([provider, providerModels]) => { visibleProviderEntries.forEach(({ provider, models: providerModels, isCollapsed }) => {
options.push({ type: "provider", value: `__group_${provider}`, label: provider, provider }); options.push({ type: "provider", value: `__group_${provider}`, label: provider, provider });
providerModels.forEach((m) => { if (isCollapsed) return;
providerModels.forEach((model) => {
options.push({ options.push({
type: "model", type: "model",
value: `${m.provider}/${m.id}`, value: `${model.provider}/${model.id}`,
label: m.name, label: model.name,
provider: m.provider, provider: model.provider,
}); });
}); });
}); });
return options; return options;
}, [favoritedModelEntries, sortedProviderEntries, specialOptions]); }, [favoritedModelEntries, specialOptions, visibleProviderEntries]);
// Get current selection display text // Get current selection display text
const selectedDisplayText = useMemo(() => { const selectedDisplayText = useMemo(() => {
@@ -509,6 +546,28 @@ export function CustomModelDropdown({
searchInputRef.current?.focus(); searchInputRef.current?.focus();
}, []); }, []);
const handleToggleCollapsedProvider = useCallback((provider: string) => {
setCollapsedProviders((previous) => {
const next = new Set(previous);
if (next.has(provider)) {
next.delete(provider);
} else {
next.add(provider);
}
try {
if (typeof window !== "undefined") {
window.localStorage.setItem(COLLAPSED_PROVIDERS_STORAGE_KEY, JSON.stringify([...next].sort()));
}
} catch {
// Storage failures must not prevent the in-memory affordance from working.
}
return next;
});
setHighlightedIndex(0);
}, []);
const handleTriggerClick = useCallback(() => { const handleTriggerClick = useCallback(() => {
if (!disabled) { if (!disabled) {
setIsOpen((prev) => !prev); setIsOpen((prev) => !prev);
@@ -525,8 +584,6 @@ export function CustomModelDropdown({
} }
}, [highlightedIndex, isOpen]); }, [highlightedIndex, isOpen]);
const hasFilter = localFilter.length > 0;
const dropdownContent = isOpen && dropdownPosition ? ( const dropdownContent = isOpen && dropdownPosition ? (
<div <div
ref={dropdownRef} ref={dropdownRef}
@@ -653,18 +710,10 @@ export function CustomModelDropdown({
</> </>
)} )}
{sortedProviderEntries.map(([provider, providerModels]) => { {visibleProviderEntries.map(({ provider, models: providerModels, isCollapsed }) => {
const groupStartIndex = optionsList.findIndex((opt) => opt.value === `__group_${provider}`); const groupStartIndex = optionsList.findIndex((opt) => opt.value === `__group_${provider}`);
const isFavorite = favoriteProviders.includes(provider); const isFavorite = favoriteProviders.includes(provider);
const isExpanded = !isCollapsed;
// Filter out favorited models - they already appear in the favorites section
const nonFavoritedModels = providerModels.filter((m) => {
const optionValue = `${m.provider}/${m.id}`;
return !favoriteModels.includes(optionValue);
});
// Skip provider group if all models are favorited
if (nonFavoritedModels.length === 0) return null;
return ( return (
<div key={provider} className="model-combobox-group"> <div key={provider} className="model-combobox-group">
@@ -685,9 +734,24 @@ export function CustomModelDropdown({
★ ★
</button> </button>
)} )}
<button
type="button"
className={`model-combobox-optgroup-toggle ${isExpanded ? "model-combobox-optgroup-toggle--expanded" : ""}`}
onClick={(event) => {
event.stopPropagation();
handleToggleCollapsedProvider(provider);
}}
aria-label={isExpanded
? t("models.collapseProvider", "Collapse {{provider}}", { provider })
: t("models.expandProvider", "Expand {{provider}}", { provider })}
aria-expanded={isExpanded}
data-testid={`model-combobox-provider-toggle-${provider}`}
>
▼
</button>
</div> </div>
{nonFavoritedModels.map((m) => { {!isCollapsed && providerModels.map((model) => {
const optionValue = `${m.provider}/${m.id}`; const optionValue = `${model.provider}/${model.id}`;
const optionIndex = optionsList.findIndex((opt) => opt.value === optionValue); const optionIndex = optionsList.findIndex((opt) => opt.value === optionValue);
const isHighlighted = highlightedIndex === optionIndex; const isHighlighted = highlightedIndex === optionIndex;
const isSelected = value === optionValue; const isSelected = value === optionValue;
@@ -703,8 +767,8 @@ export function CustomModelDropdown({
role="option" role="option"
aria-selected={isSelected} aria-selected={isSelected}
> >
<span className="model-combobox-option-text">{m.name}</span> <span className="model-combobox-option-text">{model.name}</span>
<span className="model-combobox-option-id">{m.id}</span> <span className="model-combobox-option-id">{model.id}</span>
{onToggleModelFavorite && ( {onToggleModelFavorite && (
<button <button
type="button" type="button"
@@ -714,7 +778,7 @@ export function CustomModelDropdown({
onToggleModelFavorite(optionValue); onToggleModelFavorite(optionValue);
}} }}
title={isFavorited ? t("models.removeFromFavorites", "Remove from favorites") : t("models.addToFavorites", "Add to favorites")} title={isFavorited ? t("models.removeFromFavorites", "Remove from favorites") : t("models.addToFavorites", "Add to favorites")}
aria-label={isFavorited ? t("models.removeFromFavoritesAriaLabel", "Remove {{name}} from favorites", { name: m.name }) : t("models.addToFavoritesAriaLabel", "Add {{name}} to favorites", { name: m.name })} aria-label={isFavorited ? t("models.removeFromFavoritesAriaLabel", "Remove {{name}} from favorites", { name: model.name }) : t("models.addToFavoritesAriaLabel", "Add {{name}} to favorites", { name: model.name })}
> >
{isFavorited ? "★" : "☆"} {isFavorited ? "★" : "☆"}
</button> </button>

View File

@@ -15,9 +15,17 @@ const MOCK_MODELS = [
{ provider: "openai", id: "gpt-4o", name: "GPT-4o", reasoning: false, contextWindow: 128000 }, { provider: "openai", id: "gpt-4o", name: "GPT-4o", reasoning: false, contextWindow: 128000 },
]; ];
const COLLAPSIBLE_MODELS = [
{ provider: "anthropic", id: "claude-sonnet", name: "Claude Sonnet", reasoning: true, contextWindow: 200000 },
{ provider: "anthropic", id: "claude-haiku", name: "Claude Haiku", reasoning: false, contextWindow: 200000 },
{ provider: "openai", id: "gpt-4o", name: "GPT-4o", reasoning: false, contextWindow: 128000 },
{ provider: "openai", id: "gpt-4o-mini", name: "GPT-4o mini", reasoning: false, contextWindow: 128000 },
];
describe("CustomModelDropdown", () => { describe("CustomModelDropdown", () => {
beforeEach(() => { beforeEach(() => {
vi.restoreAllMocks(); vi.restoreAllMocks();
window.localStorage.clear();
document.body.innerHTML = ""; document.body.innerHTML = "";
vi.spyOn(window, "matchMedia").mockImplementation((query: string) => ({ vi.spyOn(window, "matchMedia").mockImplementation((query: string) => ({
matches: false, matches: false,
@@ -38,6 +46,127 @@ describe("CustomModelDropdown", () => {
expect(wrapperRuleMatch![0]).toContain("background: var(--surface);"); expect(wrapperRuleMatch![0]).toContain("background: var(--surface);");
}); });
it("renders provider headers inside the list with sticky positioning", async () => {
const user = userEvent.setup();
const css = loadAllAppCss();
const optgroupRule = css.match(/\.model-combobox-optgroup\s*\{[^}]*\}/)?.[0] ?? "";
expect(optgroupRule).toContain("position: sticky;");
expect(optgroupRule).toContain("top: 0;");
expect(optgroupRule).toContain("z-index: 1;");
expect(optgroupRule).toContain("background: var(--bg);");
render(<CustomModelDropdown label="Model" value="" onChange={vi.fn()} models={MOCK_MODELS} />);
await user.click(screen.getByRole("button", { name: "Model" }));
const list = screen.getByTestId("model-combobox-portal").querySelector(".model-combobox-list");
expect(list).not.toBeNull();
expect(within(list!).getByText("anthropic").closest(".model-combobox-optgroup")).not.toBeNull();
expect(within(list!).getByText("openai").closest(".model-combobox-optgroup")).not.toBeNull();
});
it("collapses provider rows, preserves special rows, and persists the preference", async () => {
const user = userEvent.setup();
render(
<CustomModelDropdown
label="Model"
value=""
onChange={vi.fn()}
models={COLLAPSIBLE_MODELS}
favoriteModels={["anthropic/claude-haiku"]}
/>,
);
await user.click(screen.getByRole("button", { name: "Model" }));
expect(screen.getByRole("button", { name: "Collapse anthropic" })).toHaveAttribute("aria-expanded", "true");
expect(screen.getByText("Claude Sonnet")).toBeTruthy();
expect(screen.getByText("Claude Haiku")).toBeTruthy();
expect(screen.getAllByText("Use default")).toHaveLength(2);
await user.click(screen.getByRole("button", { name: "Collapse anthropic" }));
expect(screen.getByRole("button", { name: "Expand anthropic" })).toHaveAttribute("aria-expanded", "false");
expect(screen.queryByText("Claude Sonnet")).toBeNull();
expect(screen.getByText("Claude Haiku")).toBeTruthy();
expect(screen.getAllByText("Use default")).toHaveLength(2);
expect(window.localStorage.getItem("fusion-dashboard-model-dropdown-collapsed-providers")).toBe('["anthropic"]');
await user.click(screen.getByRole("button", { name: "Expand anthropic" }));
expect(screen.getByText("Claude Sonnet")).toBeTruthy();
});
it("restores collapsed providers, tolerates malformed storage, and surfaces matches while filtering", async () => {
window.localStorage.setItem("fusion-dashboard-model-dropdown-collapsed-providers", '["anthropic"]');
const user = userEvent.setup();
const view = render(<CustomModelDropdown label="Model" value="" onChange={vi.fn()} models={COLLAPSIBLE_MODELS} />);
await user.click(screen.getByRole("button", { name: "Model" }));
expect(screen.queryByText("Claude Sonnet")).toBeNull();
expect(screen.getByRole("button", { name: "Expand anthropic" })).toBeTruthy();
await user.type(screen.getByPlaceholderText("Filter models…"), "sonnet");
expect(screen.getByText("Claude Sonnet")).toBeTruthy();
expect(screen.getByRole("button", { name: "Collapse anthropic" })).toHaveAttribute("aria-expanded", "true");
view.unmount();
window.localStorage.setItem("fusion-dashboard-model-dropdown-collapsed-providers", "not-json");
render(<CustomModelDropdown label="Other model" value="" onChange={vi.fn()} models={MOCK_MODELS} />);
await user.click(screen.getByRole("button", { name: "Other model" }));
expect(screen.getByText("Claude Sonnet 4.5")).toBeTruthy();
});
it("omits collapsed rows from keyboard navigation", async () => {
const user = userEvent.setup();
const onChange = vi.fn();
render(<CustomModelDropdown label="Model" value="" onChange={onChange} models={COLLAPSIBLE_MODELS} />);
await user.click(screen.getByRole("button", { name: "Model" }));
await user.click(screen.getByRole("button", { name: "Collapse anthropic" }));
expect(screen.queryByText("Claude Sonnet")).toBeNull();
await user.keyboard("{ArrowDown}{Enter}");
expect(onChange).toHaveBeenCalledWith("openai/gpt-4o");
});
it("skips collapsed provider rows when navigating upward", async () => {
const user = userEvent.setup();
const onChange = vi.fn();
render(<CustomModelDropdown label="Model" value="" onChange={onChange} models={COLLAPSIBLE_MODELS} />);
await user.click(screen.getByRole("button", { name: "Model" }));
await user.click(screen.getByRole("button", { name: "Collapse anthropic" }));
await user.keyboard("{ArrowDown}{ArrowUp}{Enter}");
expect(onChange).toHaveBeenCalledWith("");
});
it("hides toggles for fully favorited provider groups", async () => {
const user = userEvent.setup();
render(
<CustomModelDropdown
label="Model"
value=""
onChange={vi.fn()}
models={COLLAPSIBLE_MODELS}
favoriteModels={["anthropic/claude-sonnet", "anthropic/claude-haiku"]}
/>,
);
await user.click(screen.getByRole("button", { name: "Model" }));
expect(screen.queryByTestId("model-combobox-provider-toggle-anthropic")).toBeNull();
expect(screen.getByText("Claude Sonnet")).toBeTruthy();
});
it("keeps empty model lists free of provider toggles", async () => {
const user = userEvent.setup();
render(<CustomModelDropdown label="Model" value="" onChange={vi.fn()} models={[]} />);
await user.click(screen.getByRole("button", { name: "Model" }));
expect(screen.queryByTestId(/model-combobox-provider-toggle-/)).toBeNull();
expect(screen.getAllByText("Use default")).toHaveLength(2);
});
it("keeps CustomModelDropdown.css scoped to .model-combobox selectors", () => { it("keeps CustomModelDropdown.css scoped to .model-combobox selectors", () => {
const css = readFileSync( const css = readFileSync(
resolve(__dirname, "../CustomModelDropdown.css"), resolve(__dirname, "../CustomModelDropdown.css"),