feat(FN-922): replace subtask size buttons with dropdown and improve modal UX
- Replace size selection buttons (S/M/L) with a clean dropdown in subtask breakdown dialog - Increase description textarea height for better editing experience - Clean up orphaned CSS from removed size button styles - Fix fragile size selection test to use proper dropdown interaction
This commit is contained in:
@@ -92,17 +92,39 @@ describe("SubtaskBreakdownModal", () => {
|
||||
await waitFor(() => expect(screen.queryByDisplayValue("First")).not.toBeInTheDocument());
|
||||
});
|
||||
|
||||
it("renders description textarea with 8 rows", async () => {
|
||||
renderModal();
|
||||
await waitFor(() => expect(streamHandlers).toBeDefined());
|
||||
streamHandlers.onSubtasks(SAMPLE_SUBTASKS);
|
||||
const textareas = await screen.findAllByRole("textbox");
|
||||
const descriptionTextarea = textareas.find((t) => t.tagName === "TEXTAREA");
|
||||
expect(descriptionTextarea).toHaveAttribute("rows", "8");
|
||||
});
|
||||
|
||||
it("changes size and dependency selection", async () => {
|
||||
renderModal();
|
||||
await waitFor(() => expect(streamHandlers).toBeDefined());
|
||||
streamHandlers.onSubtasks(SAMPLE_SUBTASKS);
|
||||
|
||||
fireEvent.click(await screen.findAllByText("L").then((buttons) => buttons[0]!));
|
||||
const selects = await screen.findAllByRole("combobox");
|
||||
fireEvent.change(selects[0], { target: { value: "L" } });
|
||||
// Use findAllByText to get all occurrences of subtask-1 and check the first one
|
||||
const subtaskLabels = await screen.findAllByText("subtask-1");
|
||||
expect(subtaskLabels.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("changes size via dropdown selection", async () => {
|
||||
renderModal();
|
||||
await waitFor(() => expect(streamHandlers).toBeDefined());
|
||||
streamHandlers.onSubtasks(SAMPLE_SUBTASKS);
|
||||
const selects = await screen.findAllByRole("combobox");
|
||||
expect(selects.length).toBeGreaterThan(0);
|
||||
fireEvent.change(selects[0], { target: { value: "L" } });
|
||||
// Verify state was updated (the API call will receive the updated value)
|
||||
fireEvent.click(screen.getByText("Create Tasks"));
|
||||
await waitFor(() => expect(mockCreateTasksFromBreakdown).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it("saves via API with edited data", async () => {
|
||||
renderModal();
|
||||
await waitFor(() => expect(streamHandlers).toBeDefined());
|
||||
|
||||
@@ -473,7 +473,7 @@ export function SubtaskBreakdownModal({ isOpen, onClose, initialDescription, onT
|
||||
<div className="form-group">
|
||||
<label>Description</label>
|
||||
<textarea
|
||||
rows={3}
|
||||
rows={8}
|
||||
value={subtask.description}
|
||||
onChange={(event) => updateSubtask(subtask.id, { description: event.target.value })}
|
||||
disabled={view.type === "creating"}
|
||||
@@ -482,19 +482,16 @@ export function SubtaskBreakdownModal({ isOpen, onClose, initialDescription, onT
|
||||
|
||||
<div className="form-group">
|
||||
<label>Size</label>
|
||||
<div className="planning-size-selector">
|
||||
{(["S", "M", "L"] as const).map((size) => (
|
||||
<button
|
||||
key={size}
|
||||
type="button"
|
||||
className={`planning-size-btn ${subtask.suggestedSize === size ? "selected" : ""}`}
|
||||
onClick={() => updateSubtask(subtask.id, { suggestedSize: size })}
|
||||
disabled={view.type === "creating"}
|
||||
>
|
||||
{size}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<select
|
||||
className="planning-size-select"
|
||||
value={subtask.suggestedSize}
|
||||
onChange={(event) => updateSubtask(subtask.id, { suggestedSize: event.target.value as "S" | "M" | "L" })}
|
||||
disabled={view.type === "creating"}
|
||||
>
|
||||
<option value="S">S</option>
|
||||
<option value="M">M</option>
|
||||
<option value="L">L</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
|
||||
@@ -11190,48 +11190,43 @@ html .column.drag-over * {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.planning-size-selector {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: var(--space-md);
|
||||
}
|
||||
|
||||
.planning-size-btn {
|
||||
min-width: 0;
|
||||
padding: 14px 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--space-xs);
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background var(--transition-fast),
|
||||
border-color var(--transition-fast),
|
||||
box-shadow var(--transition-fast),
|
||||
transform var(--transition-fast);
|
||||
}
|
||||
|
||||
.planning-size-btn:hover {
|
||||
background: var(--card-hover);
|
||||
border-color: var(--todo);
|
||||
box-shadow: var(--focus-ring);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.planning-size-btn.selected {
|
||||
background: var(--card-hover);
|
||||
border-color: var(--todo);
|
||||
box-shadow: inset 0 0 0 1px var(--todo);
|
||||
}
|
||||
|
||||
.planning-size-label {
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Taller description textarea in subtask breakdown */
|
||||
.planning-summary-form .form-group textarea {
|
||||
min-height: 120px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/* Compact size dropdown */
|
||||
.planning-size-select {
|
||||
width: 100%;
|
||||
padding: 10px 36px 10px 12px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--text);
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 10px center;
|
||||
}
|
||||
|
||||
.planning-size-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--todo);
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
.planning-size-select:hover:not(:focus) {
|
||||
border-color: var(--text-muted);
|
||||
}
|
||||
|
||||
.planning-deps-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -11544,15 +11539,6 @@ html .column.drag-over * {
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.planning-size-selector {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.planning-size-btn {
|
||||
min-height: 72px;
|
||||
padding: 12px 10px;
|
||||
}
|
||||
|
||||
.planning-deps-list {
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
Reference in New Issue
Block a user