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:
gsxdsm
2026-06-28 09:09:03 -07:00
parent b5ed4e0098
commit 8d3c15eb10
7 changed files with 83 additions and 18 deletions

View File

@@ -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.

View File

@@ -36,6 +36,12 @@
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.is-active {
background: var(--card-hover);

View File

@@ -482,24 +482,6 @@
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 {
display: flex;
gap: var(--space-sm);

View File

@@ -78,6 +78,16 @@ describe("WorkflowOptionalStepsDropdown CSS", () => {
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", () => {
expect(DROPDOWN_CSS).not.toMatch(/var\(--[^,]+,\s*#/);
expect(DROPDOWN_CSS).not.toMatch(/var\(--[^,]+,\s*\d+px/);

View File

@@ -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*\{/);
});
});

View 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);
}

View File

@@ -10,6 +10,8 @@
import type { ReactNode } from "react";
import type { useTranslation } from "react-i18next";
import "./workflow-phase-badge.css";
export function phaseBadge(
phase: "pre-merge" | "post-merge",
id: string,