FN-7034: Align steps dropdown trigger styling
Align the optional steps dropdown trigger with shared task creation button styling. - Reuse the dashboard btn btn-sm classes for the workflow optional steps trigger. - Remove bespoke trigger button styling so shared button tokens control padding, border, radius, and states. - Extend dropdown tests to cover shared classes across empty, selected, and disabled states. - Add a patch changeset for the published Fusion package. Files changed: .changeset/fn-7034-steps-dropdown-style.md | 7 +++ .../components/WorkflowOptionalStepsDropdown.css | 14 ------ .../components/WorkflowOptionalStepsDropdown.tsx | 9 +++- .../WorkflowOptionalStepsDropdown.test.tsx | 50 ++++++++++++++++++++-- 4 files changed, 61 insertions(+), 19 deletions(-) Fusion-Task-Id: FN-7034 Fusion-Task-Lineage: 3ca2a726-2c67-44a0-9a88-467e4fc1ad6b
This commit is contained in:
7
.changeset/fn-7034-steps-dropdown-style.md
Normal file
7
.changeset/fn-7034-steps-dropdown-style.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Match the optional steps dropdown trigger to shared task creation buttons.
|
||||||
|
category: fix
|
||||||
|
dev: Reuses the dashboard `.btn .btn-sm` trigger styling for WorkflowOptionalStepsDropdown.
|
||||||
@@ -6,20 +6,6 @@
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wf-optional-steps-dropdown-trigger {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 6px;
|
|
||||||
padding: 4px 8px;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: var(--radius-sm, 6px);
|
|
||||||
background: var(--surface, transparent);
|
|
||||||
color: inherit;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wf-optional-steps-dropdown-trigger:disabled {
|
.wf-optional-steps-dropdown-trigger:disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|||||||
@@ -16,6 +16,11 @@
|
|||||||
* matching the quick-add card's prior no-chip-block behavior and the modal's
|
* matching the quick-add card's prior no-chip-block behavior and the modal's
|
||||||
* empty-state choice, so both surfaces look identical.
|
* empty-state choice, so both surfaces look identical.
|
||||||
*
|
*
|
||||||
|
* FNXC:TaskCreationButtons 2026-06-25-00:00:
|
||||||
|
* The optional-steps trigger must reuse shared `.btn .btn-sm` styling so it has
|
||||||
|
* the same padding, background, border, radius, and interaction affordances as
|
||||||
|
* the quick-add and New Task action buttons on every creation surface.
|
||||||
|
*
|
||||||
* Accessibility: trigger has aria-haspopup/aria-expanded; the panel is a
|
* Accessibility: trigger has aria-haspopup/aria-expanded; the panel is a
|
||||||
* role="listbox" labelled by the trigger; each option is a role="option" with
|
* role="listbox" labelled by the trigger; each option is a role="option" with
|
||||||
* aria-checked. Escape closes and refocuses the trigger; arrow keys move the
|
* aria-checked. Escape closes and refocuses the trigger; arrow keys move the
|
||||||
@@ -145,7 +150,7 @@ export function WorkflowOptionalStepsDropdown({
|
|||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
type="button"
|
type="button"
|
||||||
id={labelId}
|
id={labelId}
|
||||||
className="wf-optional-steps-dropdown-trigger"
|
className="btn btn-sm wf-optional-steps-dropdown-trigger"
|
||||||
data-testid={triggerTestId}
|
data-testid={triggerTestId}
|
||||||
aria-haspopup="listbox"
|
aria-haspopup="listbox"
|
||||||
aria-expanded={isOpen}
|
aria-expanded={isOpen}
|
||||||
@@ -157,7 +162,7 @@ export function WorkflowOptionalStepsDropdown({
|
|||||||
onKeyDown={onTriggerKeyDown}
|
onKeyDown={onTriggerKeyDown}
|
||||||
>
|
>
|
||||||
<span>{triggerLabel}</span>
|
<span>{triggerLabel}</span>
|
||||||
<ChevronDown size={13} aria-hidden />
|
<ChevronDown size={12} aria-hidden />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{isOpen &&
|
{isOpen &&
|
||||||
|
|||||||
@@ -13,8 +13,27 @@ const STEP: ResolvedWorkflowOptionalStep = {
|
|||||||
defaultOn: false,
|
defaultOn: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const STEP_TWO: ResolvedWorkflowOptionalStep = {
|
||||||
|
templateId: "test-review",
|
||||||
|
name: "Test Review",
|
||||||
|
description: "Review test coverage",
|
||||||
|
icon: "check-circle",
|
||||||
|
phase: "post-implementation",
|
||||||
|
defaultOn: false,
|
||||||
|
};
|
||||||
|
|
||||||
// Controlled host: parent owns the enabled set, mirroring the create surfaces.
|
// Controlled host: parent owns the enabled set, mirroring the create surfaces.
|
||||||
function Host({ steps, initial = [] }: { steps: ResolvedWorkflowOptionalStep[]; initial?: string[] }) {
|
function Host({
|
||||||
|
steps,
|
||||||
|
initial = [],
|
||||||
|
disabled = false,
|
||||||
|
triggerTestId,
|
||||||
|
}: {
|
||||||
|
steps: ResolvedWorkflowOptionalStep[];
|
||||||
|
initial?: string[];
|
||||||
|
disabled?: boolean;
|
||||||
|
triggerTestId?: string;
|
||||||
|
}) {
|
||||||
const [enabled, setEnabled] = useState<string[]>(initial);
|
const [enabled, setEnabled] = useState<string[]>(initial);
|
||||||
return (
|
return (
|
||||||
<WorkflowOptionalStepsDropdown
|
<WorkflowOptionalStepsDropdown
|
||||||
@@ -23,10 +42,18 @@ function Host({ steps, initial = [] }: { steps: ResolvedWorkflowOptionalStep[];
|
|||||||
onToggle={(id) =>
|
onToggle={(id) =>
|
||||||
setEnabled((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]))
|
setEnabled((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]))
|
||||||
}
|
}
|
||||||
|
disabled={disabled}
|
||||||
|
triggerTestId={triggerTestId}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function expectSharedButtonTrigger(trigger: HTMLElement) {
|
||||||
|
expect(trigger).toHaveClass("btn", "btn-sm", "wf-optional-steps-dropdown-trigger");
|
||||||
|
expect(trigger).toHaveAttribute("aria-haspopup", "listbox");
|
||||||
|
expect(trigger).toHaveAttribute("aria-expanded");
|
||||||
|
}
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
cleanup();
|
cleanup();
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
@@ -38,9 +65,26 @@ describe("WorkflowOptionalStepsDropdown", () => {
|
|||||||
expect(container.firstChild).toBeNull();
|
expect(container.firstChild).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reflects the selected count in the trigger label", () => {
|
it("uses shared button classes and preserves trigger attributes when none are selected", () => {
|
||||||
render(<Host steps={[STEP]} />);
|
render(<Host steps={[STEP]} triggerTestId="custom-optional-steps-trigger" />);
|
||||||
|
const trigger = screen.getByTestId("custom-optional-steps-trigger");
|
||||||
|
expectSharedButtonTrigger(trigger);
|
||||||
|
expect(trigger).toHaveTextContent("Steps: none");
|
||||||
|
expect(trigger).toHaveAttribute("aria-expanded", "false");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses shared button classes and count label when multiple steps are selected", () => {
|
||||||
|
render(<Host steps={[STEP, STEP_TWO]} initial={["browser-verification", "test-review"]} />);
|
||||||
const trigger = screen.getByTestId("wf-optional-steps-dropdown-trigger");
|
const trigger = screen.getByTestId("wf-optional-steps-dropdown-trigger");
|
||||||
|
expectSharedButtonTrigger(trigger);
|
||||||
|
expect(trigger).toHaveTextContent("Steps: 2 selected");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses shared button classes and disabled semantics when submitting", () => {
|
||||||
|
render(<Host steps={[STEP]} disabled />);
|
||||||
|
const trigger = screen.getByTestId("wf-optional-steps-dropdown-trigger");
|
||||||
|
expectSharedButtonTrigger(trigger);
|
||||||
|
expect(trigger).toBeDisabled();
|
||||||
expect(trigger).toHaveTextContent("Steps: none");
|
expect(trigger).toHaveTextContent("Steps: none");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user