Keep the Skills view toggle anchored correctly after skill enablement on mobile and desktop. - add relative positioning to the Skills view toggle wrapper so the hidden input stays aligned with its label - expand the CSS regression test to verify the hidden input anchoring contract and checked-toggle geometry selectors Files changed: packages/dashboard/app/components/SkillsView.css | 1 + .../components/__tests__/SkillsView.css.test.ts | 30 +++++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-5910 Fusion-Task-Lineage: d5988ddb-febe-4319-bfef-d0dbc37909ba
90 lines
3.5 KiB
TypeScript
90 lines
3.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { loadAllAppCss } from "../../test/cssFixture";
|
|
|
|
function extractRuleBlock(css: string, selector: string): string {
|
|
const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
const matches = [...css.matchAll(new RegExp(`${escapedSelector}\\s*\\{([^}]*)\\}`, "g"))];
|
|
return matches.at(-1)?.[1] ?? "";
|
|
}
|
|
|
|
function extractMobileMediaBlocks(content: string): string {
|
|
const blocks: string[] = [];
|
|
const regex = /@media[^{]*\(max-width: 768px\)[^{]*\{/g;
|
|
let match: RegExpExecArray | null;
|
|
|
|
while ((match = regex.exec(content)) !== null) {
|
|
const startIdx = match.index + match[0].length;
|
|
let braceCount = 1;
|
|
let endIdx = startIdx;
|
|
|
|
while (braceCount > 0 && endIdx < content.length) {
|
|
if (content[endIdx] === "{") braceCount += 1;
|
|
if (content[endIdx] === "}") braceCount -= 1;
|
|
endIdx += 1;
|
|
}
|
|
|
|
if (braceCount === 0) {
|
|
blocks.push(content.slice(startIdx, endIdx - 1));
|
|
}
|
|
}
|
|
|
|
return blocks.join("\n");
|
|
}
|
|
|
|
describe("SkillsView/runtime-card token guardrails", () => {
|
|
it("does not use forbidden runtime fallback literals/tokens", async () => {
|
|
const css = await loadAllAppCss();
|
|
|
|
expect(css).not.toContain("var(--accent-green");
|
|
expect(css).not.toContain("var(--accent-red");
|
|
expect(css).not.toContain("var(--space-xxs");
|
|
expect(css).not.toContain("var(--accent-green, #22c55e)");
|
|
expect(css).not.toContain("var(--accent-red, #ef4444)");
|
|
expect(css).not.toContain("var(--accent, #4f46e5)");
|
|
});
|
|
|
|
it("keeps discovered-skill rows on one line at the mobile breakpoint", async () => {
|
|
const css = await loadAllAppCss();
|
|
const mobileMediaBlock = extractMobileMediaBlocks(css);
|
|
const itemBlock = extractRuleBlock(mobileMediaBlock, ".skills-view-item");
|
|
const infoBlock = extractRuleBlock(mobileMediaBlock, ".skills-view-item-info");
|
|
|
|
expect(itemBlock).toContain("flex-wrap: nowrap");
|
|
expect(infoBlock).toContain("flex: 1 1 auto");
|
|
expect(infoBlock).toContain("width: auto");
|
|
});
|
|
|
|
it("anchors the hidden toggle input to the toggle label across desktop and mobile", async () => {
|
|
const css = await loadAllAppCss();
|
|
const toggleBlock = extractRuleBlock(css, ".skills-view-item-toggle");
|
|
const inputBlock = extractRuleBlock(css, ".skills-view-item-toggle input");
|
|
const mobileMediaBlock = extractMobileMediaBlocks(css);
|
|
const mobileToggleBlock = extractRuleBlock(mobileMediaBlock, ".skills-view-item-toggle");
|
|
|
|
expect(toggleBlock).toContain("position: relative");
|
|
expect(inputBlock).toContain("position: absolute");
|
|
expect(inputBlock).toContain("clip: rect(0, 0, 0, 0)");
|
|
expect(mobileToggleBlock).not.toMatch(/position\s*:/);
|
|
});
|
|
|
|
it("keeps checked and unchecked toggle geometry token-aligned", async () => {
|
|
const css = await loadAllAppCss();
|
|
const sliderBlock = extractRuleBlock(css, ".skills-view-toggle-slider");
|
|
const checkedSliderBlock = extractRuleBlock(
|
|
css,
|
|
".skills-view-item-toggle input:checked + .skills-view-toggle-slider"
|
|
);
|
|
const checkedKnobBlock = extractRuleBlock(
|
|
css,
|
|
".skills-view-item-toggle input:checked + .skills-view-toggle-slider::after"
|
|
);
|
|
|
|
expect(sliderBlock).toContain("width: calc(var(--space-xl) + var(--space-lg))");
|
|
expect(checkedSliderBlock).toContain("background: var(--color-success)");
|
|
expect(checkedKnobBlock).toContain(
|
|
"transform: translateX(calc(var(--space-lg) + (var(--space-xs) / 2)))"
|
|
);
|
|
});
|
|
});
|