FN-6701: theme workflow editor controls
Theme workflow editor controls and checkbox affordances with Fusion tokens. - Add scoped React Flow zoom control and mini-map theme overrides for the workflow editor canvas. - Apply the workflow accent token to workflow settings, field, and column-trait checkboxes. - Cover the CSS contract with assertions against hardcoded white backgrounds and missing token usage. Files changed: .../app/components/WorkflowFieldsPanel.css | 8 +++ .../app/components/WorkflowNodeEditor.css | 55 +++++++++++++++++++ .../app/components/WorkflowSettingsPanel.css | 8 +++ .../__tests__/WorkflowNodeEditor.css.test.ts | 64 ++++++++++++++++++++++ 4 files changed, 135 insertions(+) Fusion-Task-Id: FN-6701 Fusion-Task-Lineage: e2e510ca-e1f7-408b-b113-ec30c9a3763a
This commit is contained in:
@@ -124,6 +124,14 @@
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:WorkflowEditorTheme 2026-06-19-05:22:
|
||||
Field-list checkboxes share the editor sidebar surface and must stay theme-accented across editable and built-in read-only workflow states.
|
||||
*/
|
||||
.wf-field--checkbox input[type="checkbox"] {
|
||||
accent-color: var(--todo);
|
||||
}
|
||||
|
||||
.wf-field-required {
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -568,6 +568,53 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:WorkflowEditorTheme 2026-06-19-05:22:
|
||||
React Flow ships white default controls and mini-map chrome, but the workflow editor must inherit Fusion theme tokens in both dark and light modes. Scope overrides to the canvas so other React Flow consumers keep their local contracts.
|
||||
*/
|
||||
.wf-editor-canvas .react-flow__controls {
|
||||
background: var(--surface);
|
||||
border: var(--btn-border-width) solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
box-shadow: var(--shadow-sm);
|
||||
color: var(--text);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wf-editor-canvas .react-flow__controls-button {
|
||||
background: var(--surface);
|
||||
border: 0;
|
||||
border-bottom: var(--btn-border-width) solid var(--border);
|
||||
color: var(--text);
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.wf-editor-canvas .react-flow__controls-button:hover {
|
||||
background: var(--surface-hover);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.wf-editor-canvas .react-flow__controls-button svg {
|
||||
fill: currentColor;
|
||||
stroke: currentColor;
|
||||
}
|
||||
|
||||
.wf-editor-canvas .react-flow__minimap {
|
||||
background: var(--surface);
|
||||
border: var(--btn-border-width) solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.wf-editor-canvas .react-flow__minimap-node {
|
||||
fill: var(--bg-secondary);
|
||||
stroke: var(--border);
|
||||
}
|
||||
|
||||
.wf-editor-canvas .react-flow__minimap-mask {
|
||||
fill: color-mix(in srgb, var(--surface) 70%, transparent);
|
||||
}
|
||||
|
||||
.wf-mobile-shell {
|
||||
display: none;
|
||||
}
|
||||
@@ -1483,6 +1530,14 @@
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:WorkflowEditorTheme 2026-06-19-05:22:
|
||||
Column trait toggles are left-sidebar workflow controls; keep their enabled and disabled checkbox chrome on the workflow accent token instead of React/browser defaults.
|
||||
*/
|
||||
.wf-column-trait input[type="checkbox"] {
|
||||
accent-color: var(--todo);
|
||||
}
|
||||
|
||||
.wf-column-traits-label,
|
||||
.wf-column-agent-label {
|
||||
font-size: 0.65rem;
|
||||
|
||||
@@ -193,6 +193,14 @@
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:WorkflowEditorTheme 2026-06-19-05:22:
|
||||
Boolean workflow settings live in the editor's left sidebar, so their checkboxes must use the same accent token as inspector checkboxes instead of browser-default white controls.
|
||||
*/
|
||||
.wf-setting--checkbox input[type="checkbox"] {
|
||||
accent-color: var(--todo);
|
||||
}
|
||||
|
||||
.wf-setting-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -37,6 +37,70 @@ function findRule(blocks: string[], selector: RegExp): string {
|
||||
return rule;
|
||||
}
|
||||
|
||||
function expectNoHardcodedWhiteBackground(rule: string): void {
|
||||
expect(rule).not.toMatch(/background(?:-color)?\s*:[^;]*(?:#fff|#ffffff|\bwhite\b)/i);
|
||||
}
|
||||
|
||||
describe("WorkflowNodeEditor themed React Flow CSS contract", () => {
|
||||
it("FN-6701 themes zoom controls, mini-map, and sidebar checkboxes with tokens", () => {
|
||||
const baseCss = loadAllAppCssBaseOnly();
|
||||
|
||||
// Surface Enumeration: WorkflowNodeEditor.tsx is the only React Flow <Controls /> / <MiniMap pannable zoomable /> mount; WorkflowResultsTab and MobileWorkflowGraphView do not mount those affordances. Left-sidebar checkbox surfaces are WorkflowSettingsPanel, WorkflowFieldsPanel, and WorkflowColumnPanel trait toggles; inspector .wf-field--checkbox stays covered by WorkflowNodeEditor.css.
|
||||
const controlsRule = findRule([baseCss], /\.wf-editor-canvas \.react-flow__controls\s*\{[^}]*\}/);
|
||||
expect(controlsRule).toMatch(/background\s*:\s*var\(--surface\)\s*;/);
|
||||
expect(controlsRule).toMatch(/border\s*:\s*var\(--btn-border-width\) solid var\(--border\)\s*;/);
|
||||
expect(controlsRule).toMatch(/color\s*:\s*var\(--text\)\s*;/);
|
||||
expectNoHardcodedWhiteBackground(controlsRule);
|
||||
|
||||
const controlsButtonRule = findRule([baseCss], /\.wf-editor-canvas \.react-flow__controls-button\s*\{[^}]*\}/);
|
||||
expect(controlsButtonRule).toMatch(/background\s*:\s*var\(--surface\)\s*;/);
|
||||
expect(controlsButtonRule).toMatch(/border-bottom\s*:\s*var\(--btn-border-width\) solid var\(--border\)\s*;/);
|
||||
expect(controlsButtonRule).toMatch(/color\s*:\s*var\(--text\)\s*;/);
|
||||
expect(controlsButtonRule).toMatch(/fill\s*:\s*currentColor\s*;/);
|
||||
expectNoHardcodedWhiteBackground(controlsButtonRule);
|
||||
|
||||
const controlsButtonHoverRule = findRule(
|
||||
[baseCss],
|
||||
/\.wf-editor-canvas \.react-flow__controls-button:hover\s*\{[^}]*\}/,
|
||||
);
|
||||
expect(controlsButtonHoverRule).toMatch(/background\s*:\s*var\(--surface-hover\)\s*;/);
|
||||
expect(controlsButtonHoverRule).toMatch(/color\s*:\s*var\(--text\)\s*;/);
|
||||
expectNoHardcodedWhiteBackground(controlsButtonHoverRule);
|
||||
|
||||
const controlsSvgRule = findRule([baseCss], /\.wf-editor-canvas \.react-flow__controls-button svg\s*\{[^}]*\}/);
|
||||
expect(controlsSvgRule).toMatch(/fill\s*:\s*currentColor\s*;/);
|
||||
expect(controlsSvgRule).toMatch(/stroke\s*:\s*currentColor\s*;/);
|
||||
|
||||
const minimapRule = findRule([baseCss], /\.wf-editor-canvas \.react-flow__minimap\s*\{[^}]*\}/);
|
||||
expect(minimapRule).toMatch(/background\s*:\s*var\(--surface\)\s*;/);
|
||||
expect(minimapRule).toMatch(/border\s*:\s*var\(--btn-border-width\) solid var\(--border\)\s*;/);
|
||||
expectNoHardcodedWhiteBackground(minimapRule);
|
||||
|
||||
const minimapNodeRule = findRule([baseCss], /\.wf-editor-canvas \.react-flow__minimap-node\s*\{[^}]*\}/);
|
||||
expect(minimapNodeRule).toMatch(/fill\s*:\s*var\(--bg-secondary\)\s*;/);
|
||||
expect(minimapNodeRule).toMatch(/stroke\s*:\s*var\(--border\)\s*;/);
|
||||
|
||||
const minimapMaskRule = findRule([baseCss], /\.wf-editor-canvas \.react-flow__minimap-mask\s*\{[^}]*\}/);
|
||||
expect(minimapMaskRule).toMatch(/fill\s*:\s*color-mix\(in srgb, var\(--surface\) 70%, transparent\)\s*;/);
|
||||
|
||||
for (const selector of [
|
||||
/\.wf-setting--checkbox input\[type="checkbox"\]\s*\{[^}]*\}/,
|
||||
/\.wf-field--checkbox input\[type="checkbox"\]\s*\{[^}]*\}/,
|
||||
/\.wf-column-trait input\[type="checkbox"\]\s*\{[^}]*\}/,
|
||||
]) {
|
||||
const checkboxRule = findRule([baseCss], selector);
|
||||
expect(checkboxRule).toMatch(/accent-color\s*:\s*var\(--todo\)\s*;/);
|
||||
expectNoHardcodedWhiteBackground(checkboxRule);
|
||||
}
|
||||
|
||||
const lightThemeOverrides = [...baseCss.matchAll(/\[data-theme="light"\][^{]*\{[^}]*\}/g)].map((match) => match[0]);
|
||||
for (const overrideRule of lightThemeOverrides.filter((rule) => /react-flow__|wf-(?:setting|field|column-trait)/.test(rule))) {
|
||||
expect(overrideRule).toMatch(/var\(--/);
|
||||
expectNoHardcodedWhiteBackground(overrideRule);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("WorkflowNodeEditor edge visibility CSS contract", () => {
|
||||
it("keeps swimlane bands translucent so built-in workflow edges remain visible", () => {
|
||||
const editorCss = readComponentCss("WorkflowNodeEditor.css");
|
||||
|
||||
Reference in New Issue
Block a user