FN-8005: normalize Quick Add action icon sizes
Unify the Quick Add primary action cluster around the shared icon-only sizing treatment. - Apply btn-icon styling and 14px SVG sizing to GitHub, session advisor, priority, and Fast controls. - Cover primary cluster uniformity across mobile, toggle, and priority states. - Add a patch changeset for the Quick Add visual fix. Files changed: .changeset/fn-8005-quick-add-icon-size-parity.md | 7 +++ .../dashboard/app/components/QuickEntryBox.tsx | 19 ++++--- .../components/__tests__/QuickEntryBox.test.tsx | 63 ++++++++++++++++++++-- 3 files changed, 79 insertions(+), 10 deletions(-) Fusion-Task-Id: FN-8005 Fusion-Task-Lineage: c912fb90-1e49-4c4d-8664-4caf1489f890 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-8005-quick-add-icon-size-parity.md
Normal file
7
.changeset/fn-8005-quick-add-icon-size-parity.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Quick Add overseer, priority, fast, GitHub, and attach icons now render at one uniform size.
|
||||
category: fix
|
||||
dev: QuickEntryBox primary-group icon-only buttons (Eye/EyeOff, PriorityIcon, Zap, GitHub ProviderIcon, Paperclip) all use the icon-only btn-icon treatment (--icon-size-sm) for a uniform 14px cluster; ProviderIcon sizeMap unchanged.
|
||||
@@ -2181,6 +2181,11 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
FNXC:QuickAddAttachments 2026-06-30-00:00 (relocated 2026-07-10): the attachment affordance
|
||||
stays adjacent to Save (now immediately to its LEFT) preserving the icon-only label, hidden
|
||||
file input trigger, and pending-count badge.
|
||||
|
||||
FNXC:QuickAddActionRow 2026-07-15-00:00:
|
||||
Attach, GitHub, session advisor, Priority, and Fast are one icon-only cluster. Every control
|
||||
uses `btn-icon` so its SVG resolves to the shared `--icon-size-sm` token; do not fork
|
||||
ProviderIcon's shared size map to size this one GitHub use case.
|
||||
*/}
|
||||
<div className="quick-entry-primary-group" data-testid="quick-entry-primary-group">
|
||||
<button
|
||||
@@ -2200,7 +2205,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-sm ${effectiveGithubTracking ? "btn-primary" : ""}`}
|
||||
className={`btn btn-icon btn-sm ${effectiveGithubTracking ? "btn-primary" : ""}`}
|
||||
onClick={() => {
|
||||
setGithubTrackingOverride((prev) => !(prev ?? projectGithubTrackingDefault));
|
||||
}}
|
||||
@@ -2225,7 +2230,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
*/}
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-sm ${effectiveSessionAdvisor ? "btn-primary" : ""}`}
|
||||
className={`btn btn-icon btn-sm ${effectiveSessionAdvisor ? "btn-primary" : ""}`}
|
||||
onClick={() => {
|
||||
setSessionAdvisorOverride((prev) => {
|
||||
const currentEffective = prev ?? projectSessionAdvisorDefault;
|
||||
@@ -2239,14 +2244,14 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
title={sessionAdvisorToggleLabel}
|
||||
aria-label={sessionAdvisorToggleLabel}
|
||||
>
|
||||
{effectiveSessionAdvisor ? <Eye size={12} aria-hidden="true" /> : <EyeOff size={12} aria-hidden="true" />}
|
||||
{effectiveSessionAdvisor ? <Eye size={14} aria-hidden="true" /> : <EyeOff size={14} aria-hidden="true" />}
|
||||
</button>
|
||||
|
||||
<div className="priority-trigger-wrap" ref={priorityPickerRef}>
|
||||
<button
|
||||
type="button"
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
className="btn btn-sm dep-trigger"
|
||||
className="btn btn-icon btn-sm dep-trigger"
|
||||
data-testid="quick-entry-priority-button"
|
||||
title={priorityButtonLabel}
|
||||
aria-label={priorityButtonLabel}
|
||||
@@ -2271,7 +2276,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
}}
|
||||
>
|
||||
{/* FNXC:PriorityColorCoding 2026-07-11-00:00: The quick-add icon-only priority trigger must preview urgency color from priorityIndicator without changing its label, test id, or picker behavior. */}
|
||||
<PriorityIcon size={12} aria-hidden="true" style={{ color: getPriorityColorVar(priority) }} />
|
||||
<PriorityIcon size={14} aria-hidden="true" style={{ color: getPriorityColorVar(priority) }} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -2316,7 +2321,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-sm ${isFastMode ? "btn-primary" : ""}`}
|
||||
className={`btn btn-icon btn-sm ${isFastMode ? "btn-primary" : ""}`}
|
||||
onClick={toggleFastMode}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
aria-pressed={isFastMode}
|
||||
@@ -2324,7 +2329,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
title={fastToggleLabel}
|
||||
aria-label={fastToggleLabel}
|
||||
>
|
||||
<Zap size={12} aria-hidden="true" />
|
||||
<Zap size={14} aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
||||
@@ -416,7 +416,7 @@ function mockMobileViewport() {
|
||||
FNXC:BoardComposer 2026-07-10-12:00:
|
||||
DOM order mirrors the reorganized composer action row: the options group (subtask, deps,
|
||||
models, node, agent) comes first, followed by the right-aligned primary group (attach, GitHub,
|
||||
Priority, Fast, Save) with Save as the LAST control.
|
||||
session advisor, Priority, Fast, Save) with Save as the LAST control.
|
||||
*/
|
||||
const QUICK_ENTRY_ACTION_BUTTONS = [
|
||||
["Subtask", "subtask-button"],
|
||||
@@ -426,11 +426,20 @@ const QUICK_ENTRY_ACTION_BUTTONS = [
|
||||
["Agent", "quick-entry-agent-button"],
|
||||
["Attach", "quick-entry-attach"],
|
||||
["GitHub", "quick-entry-github-toggle"],
|
||||
["Session advisor", "quick-entry-session-advisor-toggle"],
|
||||
["Priority", "quick-entry-priority-button"],
|
||||
["Fast", "quick-entry-fast-toggle"],
|
||||
["Save", "quick-entry-save"],
|
||||
] as const;
|
||||
|
||||
const QUICK_ENTRY_PRIMARY_ICON_BUTTON_IDS = [
|
||||
"quick-entry-attach",
|
||||
"quick-entry-github-toggle",
|
||||
"quick-entry-session-advisor-toggle",
|
||||
"quick-entry-priority-button",
|
||||
"quick-entry-fast-toggle",
|
||||
] as const;
|
||||
|
||||
const QUICK_ENTRY_PRIORITY_ICON_CLASS: Record<TaskPriority, string> = {
|
||||
low: "lucide-arrow-down",
|
||||
normal: "lucide-flag",
|
||||
@@ -456,6 +465,20 @@ function expectPriorityOptionColor(priority: TaskPriority) {
|
||||
expect(icon?.getAttribute("style")).toContain(`color: ${getPriorityColorVar(priority)}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* FNXC:QuickAddActionRow 2026-07-15-00:00:
|
||||
* The primary action cluster is visually uniform only when every icon control opts into btn-icon.
|
||||
* Assert the shared treatment rather than SVG attributes because ProviderIcon's public sm size is
|
||||
* intentionally broader than this local CSS override.
|
||||
*/
|
||||
function expectQuickEntryPrimaryIconCluster() {
|
||||
for (const testId of QUICK_ENTRY_PRIMARY_ICON_BUTTON_IDS) {
|
||||
const button = screen.getByTestId(testId);
|
||||
expect(button).toHaveClass("btn-icon");
|
||||
expect(button.querySelector("svg")).not.toBeNull();
|
||||
}
|
||||
}
|
||||
|
||||
describe("QuickEntryBox", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
@@ -894,16 +917,17 @@ describe("QuickEntryBox", () => {
|
||||
expandQuickEntry();
|
||||
|
||||
const actionButtonTestIds = getActionButtonTestIdsInDomOrder();
|
||||
expect(actionButtonTestIds.slice(-5)).toEqual([
|
||||
expect(actionButtonTestIds.slice(-6)).toEqual([
|
||||
"quick-entry-attach",
|
||||
"quick-entry-github-toggle",
|
||||
"quick-entry-session-advisor-toggle",
|
||||
"quick-entry-priority-button",
|
||||
"quick-entry-fast-toggle",
|
||||
"quick-entry-save",
|
||||
]);
|
||||
|
||||
const primaryGroup = screen.getByTestId("quick-entry-primary-group");
|
||||
for (const testId of ["quick-entry-attach", "quick-entry-github-toggle", "quick-entry-priority-button", "quick-entry-fast-toggle", "quick-entry-save"]) {
|
||||
for (const testId of ["quick-entry-attach", "quick-entry-github-toggle", "quick-entry-session-advisor-toggle", "quick-entry-priority-button", "quick-entry-fast-toggle", "quick-entry-save"]) {
|
||||
expect(primaryGroup.contains(screen.getByTestId(testId))).toBe(true);
|
||||
}
|
||||
const optionsGroup = screen.getByTestId("quick-entry-options-group");
|
||||
@@ -1004,6 +1028,18 @@ describe("QuickEntryBox", () => {
|
||||
});
|
||||
}
|
||||
|
||||
it("keeps the uniform icon cluster in the shared mobile button touch-target contract", async () => {
|
||||
await renderMobileQuickEntryWithEnabledActions();
|
||||
const actions = screen.getByTestId("quick-entry-actions");
|
||||
|
||||
expectQuickEntryPrimaryIconCluster();
|
||||
for (const testId of QUICK_ENTRY_PRIMARY_ICON_BUTTON_IDS) {
|
||||
const button = screen.getByTestId(testId);
|
||||
expect(button).toHaveClass("btn");
|
||||
expect(actions.contains(button)).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("captures an SVG touch target inside the priority button and opens the picker", async () => {
|
||||
await renderMobileQuickEntryWithEnabledActions();
|
||||
const priorityButton = screen.getByTestId("quick-entry-priority-button");
|
||||
@@ -1241,6 +1277,9 @@ describe("QuickEntryBox", () => {
|
||||
case "quick-entry-github-toggle":
|
||||
expect(screen.getByTestId(testId)).toHaveAttribute("aria-pressed", "false");
|
||||
break;
|
||||
case "quick-entry-session-advisor-toggle":
|
||||
expect(screen.getByTestId(testId)).toHaveAttribute("aria-pressed", "true");
|
||||
break;
|
||||
case "quick-entry-priority-button":
|
||||
expect(await screen.findByTestId("quick-entry-priority-option-normal")).toBeTruthy();
|
||||
break;
|
||||
@@ -2162,6 +2201,24 @@ describe("QuickEntryBox", () => {
|
||||
expectQuickEntryPriorityButton("normal");
|
||||
});
|
||||
|
||||
it("keeps every primary icon control in one btn-icon cluster across toggle and priority states", () => {
|
||||
renderQuickEntryBox({});
|
||||
expandQuickEntry();
|
||||
|
||||
expectQuickEntryPrimaryIconCluster();
|
||||
fireEvent.click(screen.getByTestId("quick-entry-session-advisor-toggle"));
|
||||
fireEvent.click(screen.getByTestId("quick-entry-github-toggle"));
|
||||
fireEvent.click(screen.getByTestId("quick-entry-fast-toggle"));
|
||||
expectQuickEntryPrimaryIconCluster();
|
||||
|
||||
for (const taskPriority of TASK_PRIORITIES) {
|
||||
openPriorityMenu();
|
||||
fireEvent.click(screen.getByTestId(`quick-entry-priority-option-${taskPriority}`));
|
||||
expectQuickEntryPriorityButton(taskPriority);
|
||||
expectQuickEntryPrimaryIconCluster();
|
||||
}
|
||||
});
|
||||
|
||||
it("renders urgency-colored priority glyphs in the trigger and picker for every level", () => {
|
||||
renderQuickEntryBox({});
|
||||
expandQuickEntry();
|
||||
|
||||
Reference in New Issue
Block a user