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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -455,6 +455,7 @@ export function NewTaskModal({ isOpen, onClose, projectId, tasks, onCreateTask,
|
||||
onPriorityChange={setPriority}
|
||||
renderBelowPrimary={quickFields}
|
||||
hideDependencies={true}
|
||||
autoExpandMoreOptionsOnSelection={false}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -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 */}
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -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([
|
||||
|
||||
Reference in New Issue
Block a user