FN-7296: show quick-add workflow icons
Improve the quick-add workflow selector so workflow choices are easier to identify before creating a task. - Render shared workflow icons in the quick-add trigger and workflow option list. - Widen the workflow selector/menu with tokenized responsive CSS while avoiding empty custom icon shells. - Cover built-in, custom, and iconless workflow rendering plus widened CSS expectations. - Add a patch changeset for the published Fusion package. Files changed: .changeset/fn-7296-quick-add-workflow-icons.md | 7 ++++ .../dashboard/app/components/QuickEntryBox.css | 45 ++++++++++++++++++++-- .../dashboard/app/components/QuickEntryBox.tsx | 15 +++++++- .../components/__tests__/QuickEntryBox.test.tsx | 41 +++++++++++++++++--- 4 files changed, 98 insertions(+), 10 deletions(-) Fusion-Task-Id: FN-7296 Fusion-Task-Lineage: e493eb27-6957-4fbd-bfb9-903f9ec43b29 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-7296-quick-add-workflow-icons.md
Normal file
7
.changeset/fn-7296-quick-add-workflow-icons.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Show workflow icons and wider names in the Quick Add workflow selector.
|
||||
category: fix
|
||||
dev: Reuses WorkflowIcon in QuickEntryBox and widens the tokenized selector/menu styles.
|
||||
@@ -138,15 +138,30 @@ The global `.description-with-refine textarea { padding-right: 70px }` (styles.c
|
||||
/*
|
||||
FNXC:QuickAddWorkflow 2026-06-30-00:00:
|
||||
Quick-add workflow targeting sits in the action row as a compact existing-button dropdown. Use tokenized sizing/colors so the selector wraps with Save/Plan controls on narrow Board and List surfaces without introducing a separate visual hierarchy.
|
||||
|
||||
FNXC:QuickAddWorkflow 2026-06-30-12:38:
|
||||
Operators need workflow identity at quick-add time. Keep the trigger wide enough for realistic names, render shared WorkflowIcon output inline, and let no-icon custom workflows collapse without blank icon shells.
|
||||
*/
|
||||
.quick-entry-workflow-wrap {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
flex: 1 1 calc(var(--space-xl) * 8);
|
||||
min-width: 0;
|
||||
max-width: min(calc(var(--space-xl) * 11), 100%);
|
||||
}
|
||||
|
||||
.quick-entry-workflow-trigger {
|
||||
max-width: calc(var(--space-xl) * 8);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: var(--space-xs);
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: min(calc(var(--space-xl) * 11), calc(100vw - var(--space-lg)));
|
||||
}
|
||||
|
||||
.quick-entry-workflow-icon {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.quick-entry-workflow-label {
|
||||
@@ -160,7 +175,8 @@ Quick-add workflow targeting sits in the action row as a compact existing-button
|
||||
position: absolute;
|
||||
inset-block-start: calc(100% + var(--space-xs));
|
||||
inset-inline-start: 0;
|
||||
min-width: min(calc(var(--space-xl) * 10), calc(100vw - var(--space-lg)));
|
||||
width: min(calc(var(--space-xl) * 16), calc(100vw - var(--space-lg)));
|
||||
min-width: min(calc(var(--space-xl) * 14), calc(100vw - var(--space-lg)));
|
||||
max-width: calc(100vw - var(--space-lg));
|
||||
max-height: min(calc(var(--space-xl) * 10), calc(100vh - var(--space-xl) * 2));
|
||||
overflow-y: auto;
|
||||
@@ -173,6 +189,28 @@ Quick-add workflow targeting sits in the action row as a compact existing-button
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.quick-entry-workflow-option-copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2xs);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.quick-entry-workflow-option-name,
|
||||
.quick-entry-workflow-option-id {
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.quick-entry-workflow-option-id {
|
||||
color: var(--text-muted);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@@ -182,7 +220,8 @@ Quick-add workflow targeting sits in the action row as a compact existing-button
|
||||
}
|
||||
|
||||
.quick-entry-workflow-menu {
|
||||
min-width: min(calc(var(--space-xl) * 9), calc(100vw - var(--space-lg)));
|
||||
width: min(calc(var(--space-xl) * 14), calc(100vw - var(--space-lg)));
|
||||
min-width: min(calc(var(--space-xl) * 11), calc(100vw - var(--space-lg)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { useNodes } from "../hooks/useNodes";
|
||||
import { NodeHealthDot } from "./NodeHealthDot";
|
||||
import { ProviderIcon } from "./ProviderIcon";
|
||||
import { WorkflowOptionalStepsDropdown } from "./WorkflowOptionalStepsDropdown";
|
||||
import { WorkflowIcon } from "./WorkflowIcon";
|
||||
|
||||
const STORAGE_KEY = "kb-quick-entry-text";
|
||||
const ALLOWED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
||||
@@ -309,6 +310,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
}, [realWorkflowOptions]);
|
||||
const showWorkflowSelector = workflowId !== undefined && realWorkflowOptions.length >= 2 && quickEntryWorkflowId !== null;
|
||||
const quickEntryWorkflowLabel = selectedQuickEntryWorkflow?.name ?? t("tasks.workflow", "Workflow");
|
||||
const selectedQuickEntryWorkflowIcon = selectedQuickEntryWorkflow?.icon;
|
||||
const selectedWorkflowForCreate = workflowId === undefined ? undefined : quickEntryWorkflowId;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -1693,6 +1695,12 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
}}
|
||||
title={t("tasks.quickEntryWorkflowTitle", "Workflow for the next task")}
|
||||
>
|
||||
<WorkflowIcon
|
||||
workflowId={selectedQuickEntryWorkflow?.id ?? quickEntryWorkflowId ?? ""}
|
||||
icon={selectedQuickEntryWorkflowIcon}
|
||||
className="quick-entry-workflow-icon"
|
||||
decorative
|
||||
/>
|
||||
<span className="quick-entry-workflow-label">{quickEntryWorkflowLabel}</span>
|
||||
<ChevronDown size={12} aria-hidden="true" />
|
||||
</button>
|
||||
@@ -1719,8 +1727,11 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
setShowWorkflowPicker(false);
|
||||
}}
|
||||
>
|
||||
<span className="dep-dropdown-title">{option.name}</span>
|
||||
{duplicateName ? <span className="dep-dropdown-subtitle">{option.id}</span> : null}
|
||||
<WorkflowIcon workflowId={option.id} icon={option.icon} className="quick-entry-workflow-icon" decorative />
|
||||
<span className="quick-entry-workflow-option-copy">
|
||||
<span className="dep-dropdown-title quick-entry-workflow-option-name">{option.name}</span>
|
||||
{duplicateName ? <span className="dep-dropdown-subtitle quick-entry-workflow-option-id">{option.id}</span> : null}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1655,6 +1655,30 @@ describe("QuickEntryBox", () => {
|
||||
expect(fetchWorkflowOptionalSteps).not.toHaveBeenCalledWith("__all_workflows__", TEST_PROJECT_ID);
|
||||
});
|
||||
|
||||
it("renders built-in and custom workflow icons without blank custom-icon shells", () => {
|
||||
renderQuickEntryBox({
|
||||
workflowId: "builtin:coding",
|
||||
defaultWorkflowId: "builtin:coding",
|
||||
workflowOptions: [
|
||||
{ id: "builtin:coding", name: "Coding", columns: [] },
|
||||
{ id: "wf-custom", name: "Custom workflow", icon: "🚀", columns: [] },
|
||||
{ id: "wf-no-icon", name: "No icon workflow", columns: [] },
|
||||
],
|
||||
});
|
||||
|
||||
const trigger = screen.getByTestId("quick-entry-workflow-trigger");
|
||||
expect(trigger.querySelector(".workflow-icon--builtin")).toBeTruthy();
|
||||
expect(trigger.querySelector(".quick-entry-workflow-label")).toHaveTextContent("Coding");
|
||||
|
||||
fireEvent.click(trigger);
|
||||
const customOption = screen.getByTestId("quick-entry-workflow-option-wf-custom");
|
||||
expect(customOption.querySelector(".workflow-icon--custom")).toHaveTextContent("🚀");
|
||||
expect(screen.getByTestId("quick-entry-workflow-option-wf-no-icon").querySelector(".workflow-icon")).toBeNull();
|
||||
|
||||
fireEvent.click(customOption);
|
||||
expect(screen.getByTestId("quick-entry-workflow-trigger").querySelector(".workflow-icon--custom")).toHaveTextContent("🚀");
|
||||
});
|
||||
|
||||
it("hides the selector without real workflow choices and leaves no shell", () => {
|
||||
renderQuickEntryBox({ workflowId: "wf-default", workflowOptions: [{ id: "wf-default", name: "Coding", columns: [] }] });
|
||||
|
||||
@@ -1662,11 +1686,18 @@ describe("QuickEntryBox", () => {
|
||||
expect(screen.getByTestId("quick-entry-actions").querySelector(".quick-entry-workflow-wrap")).toBeNull();
|
||||
});
|
||||
|
||||
it("uses tokenized responsive CSS for the workflow selector", () => {
|
||||
const selectorRule = cssRuleBody(QUICK_ENTRY_BOX_CSS, ".quick-entry-workflow-menu");
|
||||
expect(selectorRule).toContain("var(--space-");
|
||||
expect(selectorRule).not.toMatch(/#[0-9a-f]{3,8}|rgb\(/i);
|
||||
expect(QUICK_ENTRY_BOX_CSS).toMatch(/@media \(max-width: 768px\) \{[\s\S]*?quick-entry-workflow/);
|
||||
it("uses tokenized responsive CSS for the wider workflow selector", () => {
|
||||
const triggerRule = cssRuleBody(QUICK_ENTRY_BOX_CSS, ".quick-entry-workflow-trigger");
|
||||
const menuRule = cssRuleBody(QUICK_ENTRY_BOX_CSS, ".quick-entry-workflow-menu");
|
||||
const optionCopyRule = cssRuleBody(QUICK_ENTRY_BOX_CSS, ".quick-entry-workflow-option-copy");
|
||||
|
||||
expect(triggerRule).toContain("max-width: min(calc(var(--space-xl) * 11), calc(100vw - var(--space-lg)))");
|
||||
expect(triggerRule).toContain("gap: var(--space-xs)");
|
||||
expect(menuRule).toContain("width: min(calc(var(--space-xl) * 16), calc(100vw - var(--space-lg)))");
|
||||
expect(menuRule).toContain("min-width: min(calc(var(--space-xl) * 14), calc(100vw - var(--space-lg)))");
|
||||
expect(optionCopyRule).toContain("gap: var(--space-2xs)");
|
||||
expect(`${triggerRule}\n${menuRule}\n${optionCopyRule}`).not.toMatch(/#[0-9a-f]{3,8}|rgb\(|\d+px/i);
|
||||
expect(QUICK_ENTRY_BOX_CSS).toMatch(/@media \(max-width: 768px\) \{[\s\S]*?quick-entry-workflow-menu[\s\S]*?min-width: min\(calc\(var\(--space-xl\) \* 11\), calc\(100vw - var\(--space-lg\)\)\)/);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user