feat(FN-2395): style settings panel scrollbars

- Add cross-browser scrollbar styling for settings sidebar and content panes using existing design tokens
- Extend settings mobile CSS tests to assert the new scrollbar rules for WebKit and Firefox-compatible properties
- Refactor dashboard TUI app tests to render via React.createElement for compatibility with current test tooling
- Tidy mission e2e mock ID generators to satisfy formatting/quality gate checks
This commit is contained in:
Fusion
2026-04-24 21:08:35 -07:00
committed by gsxdsm
parent 70d77f8ba6
commit 20366ee250
4 changed files with 83 additions and 17 deletions

View File

@@ -155,6 +155,13 @@ function expectMobileRule(css: string, selector: string, declaration: string): v
expect(pattern.test(css)).toBe(true);
}
function expectBaseRule(css: string, selector: string, declaration: string): void {
const pattern = new RegExp(
`${escapeRegExp(selector)}\\s*\\{[^}]*${escapeRegExp(declaration)}`,
);
expect(pattern.test(css)).toBe(true);
}
describe("SettingsModal mobile adaptations", () => {
beforeEach(() => {
vi.clearAllMocks();
@@ -249,4 +256,20 @@ describe("SettingsModal mobile adaptations", () => {
expectMobileRule(css, ".settings-preset-item-actions", "justify-content: flex-start;");
expectMobileRule(css, ".settings-preset-size-grid", "grid-template-columns: 1fr;");
});
it("styles settings scrollbar rules for sidebar and content", () => {
const css = loadAllAppCss();
expectBaseRule(css, ".settings-sidebar", "scrollbar-color: var(--border) transparent;");
expectBaseRule(css, ".settings-sidebar", "scrollbar-width: thin;");
expectBaseRule(css, ".settings-sidebar::-webkit-scrollbar", "width: 6px;");
expectBaseRule(css, ".settings-sidebar::-webkit-scrollbar-thumb", "background: var(--border);");
expectBaseRule(css, ".settings-sidebar::-webkit-scrollbar-thumb:hover", "background: var(--text-muted);");
expectBaseRule(css, ".settings-content", "scrollbar-color: var(--border) transparent;");
expectBaseRule(css, ".settings-content", "scrollbar-width: thin;");
expectBaseRule(css, ".settings-content::-webkit-scrollbar", "width: 6px;");
expectBaseRule(css, ".settings-content::-webkit-scrollbar-thumb", "background: var(--border);");
expectBaseRule(css, ".settings-content::-webkit-scrollbar-thumb:hover", "background: var(--text-muted);");
});
});

View File

@@ -1556,6 +1556,25 @@ input[type="range"]:focus-visible {
padding: 10px 8px;
gap: 2px;
background: rgba(0, 0, 0, 0.1);
scrollbar-color: var(--border) transparent;
scrollbar-width: thin;
}
.settings-sidebar::-webkit-scrollbar {
width: 6px;
}
.settings-sidebar::-webkit-scrollbar-track {
background: transparent;
}
.settings-sidebar::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: var(--radius-sm);
}
.settings-sidebar::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
.settings-nav-item {
@@ -1612,6 +1631,25 @@ input[type="range"]:focus-visible {
overflow-x: hidden;
overflow-y: auto;
padding: 4px 0 12px;
scrollbar-color: var(--border) transparent;
scrollbar-width: thin;
}
.settings-content::-webkit-scrollbar {
width: 6px;
}
.settings-content::-webkit-scrollbar-track {
background: transparent;
}
.settings-content::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: var(--radius-sm);
}
.settings-content::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
.settings-content > * {
animation: settingsFadeIn var(--transition-normal);

View File

@@ -59,11 +59,11 @@ function createMockMissionStore() {
// Generate IDs matching the real MissionStore format:
// prefix + base36(timestamp) + "-" + random alphanumeric suffix
// e.g., M-MNJVKT2G-ME5Q, MS-M3N8QR-C9F1, SL-P4T2WX-D5E8, F-J6K9AB-G7H3
const generateMissionId = () => `M-MOCK${missionCounter++.toString(36).toUpperCase()}-TST`;
const generateMilestoneId = () => `MS-MOCK${milestoneCounter++.toString(36).toUpperCase()}-TST`;
const generateSliceId = () => `SL-MOCK${sliceCounter++.toString(36).toUpperCase()}-TST`;
const generateFeatureId = () => `F-MOCK${featureCounter++.toString(36).toUpperCase()}-TST`;
const generateAssertionId = () => `CA-MOCK${assertionCounter++.toString(36).toUpperCase()}-TST`;
const generateMissionId = () => `M-MOCK${(missionCounter++).toString(36).toUpperCase()}-TST`;
const generateMilestoneId = () => `MS-MOCK${(milestoneCounter++).toString(36).toUpperCase()}-TST`;
const generateSliceId = () => `SL-MOCK${(sliceCounter++).toString(36).toUpperCase()}-TST`;
const generateFeatureId = () => `F-MOCK${(featureCounter++).toString(36).toUpperCase()}-TST`;
const generateAssertionId = () => `CA-MOCK${(assertionCounter++).toString(36).toUpperCase()}-TST`;
return {
createMission: vi.fn((input: { title: string; description?: string }) => {