FN-6826: elevate the theme dropdown when open

Ensure the Command Center theme selector renders above neighboring views while preserving mobile in-flow behavior.

- Add an open state class to the theme dropdown root for local stacking control.
- Raise the open dropdown and popover above Command Center sibling cards without exceeding app chrome layers.
- Cover desktop elevation and mobile static popover behavior in ThemeDropdown tests.

Files changed:
 .../dashboard/app/components/ThemeDropdown.css     | 15 +++++++-
 .../dashboard/app/components/ThemeDropdown.tsx     |  2 +-
 .../components/__tests__/ThemeDropdown.test.tsx    | 40 ++++++++++++++++++++++
 3 files changed, 55 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-6826

Fusion-Task-Lineage: ff43c06e-f3a7-4b4c-8e29-46deb66e9145
This commit is contained in:
gsxdsm
2026-06-21 01:45:57 -07:00
parent 70653dd66b
commit 19d60480db
3 changed files with 55 additions and 2 deletions

View File

@@ -6,6 +6,14 @@
min-inline-size: 0;
}
/*
FNXC:Theme 2026-06-20-00:00:
FN-6826 requires the Command Center theme dropdown to paint on top of all sibling Command Center cards/views only while open. Use a local dropdown-tier z-index above DateRangePicker's 20 and below app chrome/mobile nav tiers, then reset it in the mobile in-flow branch so closed or static content never floats above unrelated chrome.
*/
.theme-dropdown.open {
z-index: 40;
}
.theme-dropdown-trigger {
justify-content: space-between;
gap: var(--space-sm);
@@ -43,7 +51,7 @@
.theme-dropdown-popover {
position: absolute;
z-index: 10;
z-index: 40;
inset-block-start: calc(100% + var(--space-xs));
inset-inline: 0;
padding: var(--space-sm);
@@ -85,8 +93,13 @@
}
@media (max-width: 768px) {
.theme-dropdown.open {
z-index: auto;
}
.theme-dropdown-popover {
position: static;
z-index: auto;
margin-block-start: var(--space-xs);
}

View File

@@ -105,7 +105,7 @@ export function ThemeDropdown({ colorTheme, onColorThemeChange, themeMode, onThe
};
return (
<div className="theme-dropdown" ref={rootRef}>
<div className={`theme-dropdown${open ? " open" : ""}`} ref={rootRef}>
<button
type="button"
className="theme-dropdown-trigger btn"

View File

@@ -1,3 +1,4 @@
import { readFileSync } from "node:fs";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { fireEvent, render, screen, within } from "@testing-library/react";
import { COLOR_THEMES } from "../themeOptions";
@@ -73,4 +74,43 @@ describe("ThemeDropdown", () => {
fireEvent.click(screen.getByRole("button", { name: /light/i }));
expect(onThemeModeChange).toHaveBeenCalledWith("light");
});
it.each([
["without mode controls", undefined, undefined],
["with mode controls", "dark" as const, vi.fn()],
])("elevates the open popover above Command Center sibling cards %s", (_label, themeMode, onThemeModeChange) => {
render(
<ThemeDropdown
colorTheme="default"
themeMode={themeMode}
onColorThemeChange={vi.fn()}
onThemeModeChange={onThemeModeChange}
/>,
);
const trigger = screen.getByRole("button", { name: /default/i });
const root = trigger.closest(".theme-dropdown");
expect(root).toBeTruthy();
expect(root?.classList.contains("open")).toBe(false);
expect(getComputedStyle(root!).zIndex).not.toBe("40");
fireEvent.click(trigger);
const popover = document.querySelector<HTMLElement>(".theme-dropdown-popover");
expect(trigger.getAttribute("aria-expanded")).toBe("true");
expect(root?.classList.contains("open")).toBe(true);
expect(getComputedStyle(root!).position).toBe("relative");
expect(getComputedStyle(root!).zIndex).toBe("40");
expect(popover).toBeTruthy();
expect(getComputedStyle(popover!).position).toBe("absolute");
expect(getComputedStyle(popover!).zIndex).toBe("40");
});
it("preserves the mobile static in-flow popover branch without dropdown elevation", () => {
const css = readFileSync("app/components/ThemeDropdown.css", "utf8");
expect(css).toMatch(
/@media \(max-width: 768px\) \{[\s\S]*?\.theme-dropdown\.open \{[\s\S]*?z-index: auto;[\s\S]*?\.theme-dropdown-popover \{[\s\S]*?position: static;[\s\S]*?z-index: auto;/,
);
});
});