fix(FN-2498): keep New Task more options collapsed by default

- Add an autoExpandMoreOptionsOnSelection prop to TaskForm and gate advanced-section auto-expansion behind it
- Disable auto-expansion in NewTaskModal so default-on workflow step preselection does not open More options on first render
- Mark collapsed advanced content with the hidden attribute to prevent interaction until expanded
- Add dashboard tests for default collapsed state, opt-out auto-expand behavior, and default-on workflow-step regression coverage
- Replace hardcoded spacing/transition values in NewTaskModal.css with design tokens
This commit is contained in:
Fusion
2026-04-25 11:21:35 -07:00
committed by gsxdsm
parent a8124da1ff
commit 79b73c343c
5 changed files with 93 additions and 16 deletions

View File

@@ -4,7 +4,7 @@
}
.new-task-modal .modal-body {
padding: 20px 24px;
padding: var(--space-xl);
overflow-y: auto;
max-height: calc(80vh - 120px);
flex: 1;
@@ -13,7 +13,7 @@
.new-task-modal .form-group {
margin-top: 0;
margin-bottom: 20px;
margin-bottom: var(--space-xl);
padding: 0;
}
@@ -26,7 +26,7 @@
}
.new-task-modal .checkbox-label {
margin-bottom: 4px !important;
margin-bottom: var(--space-xs) !important;
}
.task-form-primary-section {
@@ -72,7 +72,7 @@
width: fit-content;
margin-top: 0;
margin-bottom: 0;
padding: 6px;
padding: var(--space-sm);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
background: var(--bg-tertiary);
@@ -88,7 +88,7 @@
align-items: center;
justify-content: space-between;
gap: var(--space-sm);
padding: 10px 2px;
padding: var(--space-sm) var(--space-xs);
margin: 0 0 var(--space-sm);
border: none;
border-top: 1px solid var(--border-subtle);
@@ -118,18 +118,18 @@
}
.task-form-more-options {
margin-left: 6px;
padding-left: 12px;
margin-left: var(--space-sm);
padding-left: var(--space-md);
border-left: 1px solid var(--border-subtle);
overflow: hidden;
max-height: 2400px;
opacity: 1;
transition:
max-height 0.24s ease,
opacity 0.2s ease,
margin-top 0.2s ease,
padding 0.2s ease,
border-color 0.2s ease;
max-height var(--transition-slow),
opacity var(--transition-normal),
margin-top var(--transition-normal),
padding var(--transition-normal),
border-color var(--transition-normal);
}
.task-form-more-options.collapsed {
@@ -157,7 +157,7 @@
}
.task-form-more-options .form-group small {
margin-top: 6px;
margin-top: var(--space-sm);
color: var(--text-secondary, var(--text-muted));
}
@@ -267,7 +267,7 @@
.workflow-step-order-actions {
display: flex;
align-items: center;
gap: 2px;
gap: var(--space-xs);
flex-shrink: 0;
}

View File

@@ -455,6 +455,7 @@ export function NewTaskModal({ isOpen, onClose, projectId, tasks, onCreateTask,
onPriorityChange={setPriority}
renderBelowPrimary={quickFields}
hideDependencies={true}
autoExpandMoreOptionsOnSelection={false}
/>
</div>

View File

@@ -88,6 +88,8 @@ export interface TaskFormProps {
renderBelowPrimary?: React.ReactNode;
/** When true, skip rendering the Dependencies form-group inside "More options". Use when the parent renders its own dependency UI via renderBelowPrimary. */
hideDependencies?: boolean;
/** When true (default), More options auto-expands when non-default advanced selections are present. */
autoExpandMoreOptionsOnSelection?: boolean;
}
export function TaskForm({
@@ -128,6 +130,7 @@ export function TaskForm({
onClose,
renderBelowPrimary,
hideDependencies,
autoExpandMoreOptionsOnSelection = true,
reviewLevel,
onReviewLevelChange,
}: TaskFormProps) {
@@ -144,7 +147,9 @@ export function TaskForm({
reviewLevel !== undefined;
const [showDepDropdown, setShowDepDropdown] = useState(false);
const [showMoreOptions, setShowMoreOptions] = useState(hasInitialMoreOptions);
const [showMoreOptions, setShowMoreOptions] = useState(
autoExpandMoreOptionsOnSelection ? hasInitialMoreOptions : false,
);
const [depSearch, setDepSearch] = useState("");
const [availableModels, setAvailableModels] = useState<ModelInfo[]>([]);
const [favoriteProviders, setFavoriteProviders] = useState<string[]>([]);
@@ -243,11 +248,16 @@ export function TaskForm({
// Auto-expand advanced options when non-default values are present.
useEffect(() => {
if (!autoExpandMoreOptionsOnSelection) {
hadMoreOptionSelectionsRef.current = hasMoreOptionSelections;
return;
}
if (hasMoreOptionSelections && !hadMoreOptionSelectionsRef.current) {
setShowMoreOptions(true);
}
hadMoreOptionSelectionsRef.current = hasMoreOptionSelections;
}, [hasMoreOptionSelections]);
}, [hasMoreOptionSelections, autoExpandMoreOptionsOnSelection]);
// Keep dependency dropdown state clean when advanced options are collapsed.
useEffect(() => {
@@ -731,6 +741,7 @@ export function TaskForm({
id="task-form-more-options"
className={`task-form-more-options${showMoreOptions ? "" : " collapsed"}`}
aria-hidden={!showMoreOptions}
hidden={!showMoreOptions}
data-testid="task-form-more-options"
>
{/* Attachments */}

View File

@@ -97,7 +97,9 @@ describe("NewTaskModal", () => {
renderNewTaskModal();
const toggle = screen.getByTestId("task-form-more-options-toggle");
const moreOptions = screen.getByTestId("task-form-more-options");
expect(toggle).toHaveAttribute("aria-expanded", "false");
expect(moreOptions).toHaveAttribute("hidden");
// Dependencies are now in quick-fields (visible by default), so the dep-trigger is present
expect(screen.getByTestId("dep-trigger")).toBeInTheDocument();
@@ -105,6 +107,7 @@ describe("NewTaskModal", () => {
await waitFor(() => {
expect(toggle).toHaveAttribute("aria-expanded", "true");
expect(moreOptions).not.toHaveAttribute("hidden");
});
// Model Configuration, Attachments, and Workflow Steps are revealed
expect(screen.getByText(/Model Configuration/i)).toBeTruthy();
@@ -536,6 +539,32 @@ describe("NewTaskModal", () => {
// DefaultOn workflow step handling (FN-883)
describe("defaultOn workflow step handling", () => {
it("keeps More options collapsed by default even when defaultOn workflow steps are auto-applied", async () => {
const { fetchWorkflowSteps } = await import("../../api");
vi.mocked(fetchWorkflowSteps).mockResolvedValueOnce([
{ id: "WS-001", name: "QA Check", description: "Run tests", prompt: "Check tests", enabled: true, defaultOn: true, createdAt: "", updatedAt: "" },
]);
renderNewTaskModal();
await waitFor(() => {
expect(screen.getByTestId("workflow-step-checkbox-WS-001")).toBeTruthy();
});
const toggle = screen.getByTestId("task-form-more-options-toggle");
expect(toggle).toHaveAttribute("aria-expanded", "false");
fireEvent.click(toggle);
await waitFor(() => {
expect(toggle).toHaveAttribute("aria-expanded", "true");
});
expect(screen.getByText(/Model Configuration/i)).toBeTruthy();
expect(screen.getByText(/Attachments/i)).toBeTruthy();
expect(screen.getByText(/Workflow Steps/i)).toBeTruthy();
});
it("sends undefined enabledWorkflowSteps when no defaultOn steps and user hasn't interacted", async () => {
const { fetchWorkflowSteps } = await import("../../api");
vi.mocked(fetchWorkflowSteps).mockResolvedValueOnce([

View File

@@ -1116,6 +1116,42 @@ describe("TaskForm defaultOn auto-selection (FN-883)", () => {
expect(onDefaultOnApplied).toHaveBeenCalledWith(["WS-001"]);
});
it("auto-expands More options by default when advanced selections are prefilled", () => {
renderTaskForm({
mode: "create",
selectedWorkflowSteps: ["WS-001"],
});
expect(screen.getByTestId("task-form-more-options-toggle")).toHaveAttribute("aria-expanded", "true");
});
it("stays collapsed when auto-expand is disabled even with prefilled advanced selections", () => {
renderTaskForm({
mode: "create",
selectedWorkflowSteps: ["WS-001"],
autoExpandMoreOptionsOnSelection: false,
});
expect(screen.getByTestId("task-form-more-options-toggle")).toHaveAttribute("aria-expanded", "false");
});
it("hides advanced controls from interaction when More options is collapsed", () => {
renderTaskForm({ mode: "create" });
const toggle = screen.getByTestId("task-form-more-options-toggle");
const moreOptions = screen.getByTestId("task-form-more-options");
expect(toggle).toHaveAttribute("aria-expanded", "false");
expect(moreOptions).toHaveAttribute("hidden");
expect(moreOptions).toHaveAttribute("aria-hidden", "true");
fireEvent.click(toggle);
expect(toggle).toHaveAttribute("aria-expanded", "true");
expect(moreOptions).not.toHaveAttribute("hidden");
expect(moreOptions).toHaveAttribute("aria-hidden", "false");
});
it("does not auto-select workflow steps in edit mode", async () => {
const { fetchWorkflowSteps } = await import("../../api");
vi.mocked(fetchWorkflowSteps).mockResolvedValueOnce([