FN-7193: theme quick-add workflow controls
Theme quick-add optional step controls and workflow phase badges with shared dashboard tokens. - Apply the dashboard accent token to optional-step checkboxes in the workflow quick-add dropdown. - Co-locate workflow phase badge styles with the shared badge helper so all badge surfaces inherit themed colors. - Add CSS regression coverage for themed checkboxes, phase badges, and duplicate badge-style removal. - Add a patch changeset for the published Fusion package. Files changed: ...fn-7193-quick-add-steps-badge-checkbox-theme.md | 7 ++++ .../components/WorkflowOptionalStepsDropdown.css | 6 ++++ .../app/components/WorkflowResultsTab.css | 18 ---------- .../WorkflowOptionalStepsDropdown.test.tsx | 10 ++++++ .../__tests__/workflow-phase-badge.test.tsx | 41 ++++++++++++++++++++++ .../app/components/workflow-phase-badge.css | 17 +++++++++ .../app/components/workflow-phase-badge.tsx | 2 ++ 7 files changed, 83 insertions(+), 18 deletions(-) Fusion-Task-Id: FN-7193 Fusion-Task-Lineage: e1db3949-a493-4949-945d-26f567eace89 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Theme quick-add optional-step checkboxes and phase badges consistently.
|
||||||
|
category: fix
|
||||||
|
dev: Co-locates workflow phase badge CSS with the shared helper and applies the dashboard checkbox accent token.
|
||||||
@@ -36,6 +36,12 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FNXC:DashboardThemeTokens 2026-06-28-00:00: Optional-step option checkboxes must use the themed --todo accent so Quick Entry, Task Form, and Inline Create match every dashboard checkbox across dark, light, and shadcn themes. */
|
||||||
|
.wf-optional-steps-dropdown-option input[type="checkbox"] {
|
||||||
|
accent-color: var(--todo);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.wf-optional-steps-dropdown-option:hover,
|
.wf-optional-steps-dropdown-option:hover,
|
||||||
.wf-optional-steps-dropdown-option.is-active {
|
.wf-optional-steps-dropdown-option.is-active {
|
||||||
background: var(--card-hover);
|
background: var(--card-hover);
|
||||||
|
|||||||
@@ -482,24 +482,6 @@
|
|||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Phase badge base and modifier classes */
|
|
||||||
.phase-badge {
|
|
||||||
margin-inline-start: var(--space-xs);
|
|
||||||
font-size: calc(var(--space-sm) + var(--space-xs) * 0.75);
|
|
||||||
padding: var(--space-xs) var(--space-sm);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.phase-badge--pre-merge {
|
|
||||||
background-color: color-mix(in srgb, var(--ws-pre-merge) 15%, transparent);
|
|
||||||
color: var(--ws-pre-merge);
|
|
||||||
}
|
|
||||||
|
|
||||||
.phase-badge--post-merge {
|
|
||||||
background-color: color-mix(in srgb, var(--ws-post-merge) 15%, transparent);
|
|
||||||
color: var(--ws-post-merge);
|
|
||||||
}
|
|
||||||
|
|
||||||
.workflow-result-meta {
|
.workflow-result-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--space-sm);
|
gap: var(--space-sm);
|
||||||
|
|||||||
@@ -78,6 +78,16 @@ describe("WorkflowOptionalStepsDropdown CSS", () => {
|
|||||||
expect(DROPDOWN_CSS).toContain("box-shadow: var(--shadow-lg);");
|
expect(DROPDOWN_CSS).toContain("box-shadow: var(--shadow-lg);");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses the dashboard accent token for option checkboxes", () => {
|
||||||
|
const checkboxRule = DROPDOWN_CSS.match(
|
||||||
|
/\.wf-optional-steps-dropdown-option input\[type="checkbox"\]\s*\{([^}]*)\}/,
|
||||||
|
)?.[1] ?? "";
|
||||||
|
|
||||||
|
expect(checkboxRule).toContain("accent-color: var(--todo);");
|
||||||
|
expect(checkboxRule).not.toMatch(/#[0-9a-f]{3,8}\b/i);
|
||||||
|
expect(checkboxRule).not.toMatch(/\brgba?\(/i);
|
||||||
|
});
|
||||||
|
|
||||||
it("does not keep hardcoded color or px fallbacks in tokenized rules", () => {
|
it("does not keep hardcoded color or px fallbacks in tokenized rules", () => {
|
||||||
expect(DROPDOWN_CSS).not.toMatch(/var\(--[^,]+,\s*#/);
|
expect(DROPDOWN_CSS).not.toMatch(/var\(--[^,]+,\s*#/);
|
||||||
expect(DROPDOWN_CSS).not.toMatch(/var\(--[^,]+,\s*\d+px/);
|
expect(DROPDOWN_CSS).not.toMatch(/var\(--[^,]+,\s*\d+px/);
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { existsSync, readFileSync } from "node:fs";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
const BADGE_CSS_PATH = "app/components/workflow-phase-badge.css";
|
||||||
|
const BADGE_HELPER_PATH = "app/components/workflow-phase-badge.tsx";
|
||||||
|
const WORKFLOW_RESULTS_CSS_PATH = "app/components/WorkflowResultsTab.css";
|
||||||
|
|
||||||
|
const BADGE_CSS = readFileSync(BADGE_CSS_PATH, "utf8");
|
||||||
|
const BADGE_HELPER = readFileSync(BADGE_HELPER_PATH, "utf8");
|
||||||
|
const WORKFLOW_RESULTS_CSS = readFileSync(WORKFLOW_RESULTS_CSS_PATH, "utf8");
|
||||||
|
|
||||||
|
function cssRule(selector: string): string {
|
||||||
|
const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
return BADGE_CSS.match(new RegExp(`${escapedSelector}\\s*\\{([^}]*)\\}`))?.[1] ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("workflow phase badge CSS", () => {
|
||||||
|
it("co-locates the shared phase badge styles with the helper", () => {
|
||||||
|
expect(existsSync(BADGE_CSS_PATH)).toBe(true);
|
||||||
|
expect(BADGE_HELPER).toContain('import "./workflow-phase-badge.css";');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("themes pre-merge and post-merge badges with workflow phase tokens only", () => {
|
||||||
|
const preMergeRule = cssRule(".phase-badge--pre-merge");
|
||||||
|
const postMergeRule = cssRule(".phase-badge--post-merge");
|
||||||
|
|
||||||
|
expect(preMergeRule).toContain("background-color: color-mix(in srgb, var(--ws-pre-merge) 15%, transparent);");
|
||||||
|
expect(preMergeRule).toContain("color: var(--ws-pre-merge);");
|
||||||
|
expect(postMergeRule).toContain("background-color: color-mix(in srgb, var(--ws-post-merge) 15%, transparent);");
|
||||||
|
expect(postMergeRule).toContain("color: var(--ws-post-merge);");
|
||||||
|
|
||||||
|
for (const rule of [preMergeRule, postMergeRule]) {
|
||||||
|
expect(rule).not.toMatch(/#[0-9a-f]{3,8}\b/i);
|
||||||
|
expect(rule).not.toMatch(/\brgba?\(/i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes phase badge styling from WorkflowResultsTab to avoid duplicate sources", () => {
|
||||||
|
expect(WORKFLOW_RESULTS_CSS).not.toMatch(/\.phase-badge(?:--pre-merge|--post-merge)?\s*\{/);
|
||||||
|
});
|
||||||
|
});
|
||||||
17
packages/dashboard/app/components/workflow-phase-badge.css
Normal file
17
packages/dashboard/app/components/workflow-phase-badge.css
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/* FNXC:WorkflowPhaseBadge 2026-06-28-00:00: Phase badge CSS is co-located with the shared phaseBadge() helper so every surface that renders the badge stays themed, including the quick-add drop-down, New Task dialog, inline create, and workflow results instead of only the lazy WorkflowResultsTab. */
|
||||||
|
.phase-badge {
|
||||||
|
margin-inline-start: var(--space-xs);
|
||||||
|
font-size: calc(var(--space-sm) + var(--space-xs) * 0.75);
|
||||||
|
padding: var(--space-xs) var(--space-sm);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-badge--pre-merge {
|
||||||
|
background-color: color-mix(in srgb, var(--ws-pre-merge) 15%, transparent);
|
||||||
|
color: var(--ws-pre-merge);
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-badge--post-merge {
|
||||||
|
background-color: color-mix(in srgb, var(--ws-post-merge) 15%, transparent);
|
||||||
|
color: var(--ws-post-merge);
|
||||||
|
}
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import type { useTranslation } from "react-i18next";
|
import type { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import "./workflow-phase-badge.css";
|
||||||
|
|
||||||
export function phaseBadge(
|
export function phaseBadge(
|
||||||
phase: "pre-merge" | "post-merge",
|
phase: "pre-merge" | "post-merge",
|
||||||
id: string,
|
id: string,
|
||||||
|
|||||||
Reference in New Issue
Block a user