fix(KB-165): resolve planning modal autostart in StrictMode
- Fix double-render bug by using ref for hasAutoStarted instead of state\n- Enable auto-start planning tests in App.test.tsx and ListView.test.tsx\n- Add changeset for the planning modal autostart fix\n- Add comprehensive tests for planning mode auto-start behavior
This commit is contained in:
5
.changeset/fix-planning-modal-autostart.md
Normal file
5
.changeset/fix-planning-modal-autostart.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@kb/dashboard": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix PlanningModeModal auto-start functionality when initialPlan prop is provided. The auto-start now works correctly in React StrictMode.
|
||||||
@@ -178,7 +178,7 @@ describe("PlanningModeModal", () => {
|
|||||||
expect(screen.getByText(/Build a user authentication/)).toBeDefined();
|
expect(screen.getByText(/Build a user authentication/)).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip("auto-starts planning when initialPlan prop is provided", async () => {
|
it("auto-starts planning when initialPlan prop is provided", async () => {
|
||||||
render(
|
render(
|
||||||
<PlanningModeModal
|
<PlanningModeModal
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
@@ -200,7 +200,7 @@ describe("PlanningModeModal", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip("sets initial plan text in textarea when initialPlan prop is provided", async () => {
|
it("sets initial plan text in textarea when initialPlan prop is provided", async () => {
|
||||||
render(
|
render(
|
||||||
<PlanningModeModal
|
<PlanningModeModal
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
|
|||||||
@@ -42,7 +42,11 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, tasks, initi
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [responseHistory, setResponseHistory] = useState<QuestionResponse[]>([]);
|
const [responseHistory, setResponseHistory] = useState<QuestionResponse[]>([]);
|
||||||
const [editedSummary, setEditedSummary] = useState<PlanningSummary | null>(null);
|
const [editedSummary, setEditedSummary] = useState<PlanningSummary | null>(null);
|
||||||
const [hasAutoStarted, setHasAutoStarted] = useState(false);
|
// Use ref instead of state for hasAutoStarted to handle React StrictMode double-render.
|
||||||
|
// In StrictMode, components render twice but state persists across renders,
|
||||||
|
// which would skip auto-start on the second (committed) render. Refs are
|
||||||
|
// re-initialized on each render, ensuring the auto-start effect runs correctly.
|
||||||
|
const hasAutoStartedRef = useRef(false);
|
||||||
const [streamingOutput, setStreamingOutput] = useState<string>("");
|
const [streamingOutput, setStreamingOutput] = useState<string>("");
|
||||||
const [showThinking, setShowThinking] = useState(true);
|
const [showThinking, setShowThinking] = useState(true);
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
@@ -58,21 +62,21 @@ export function PlanningModeModal({ isOpen, onClose, onTaskCreated, tasks, initi
|
|||||||
|
|
||||||
// Auto-start planning when initialPlan prop is provided
|
// Auto-start planning when initialPlan prop is provided
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen && initialPlanProp && !hasAutoStarted && view.type === "initial") {
|
if (isOpen && initialPlanProp && !hasAutoStartedRef.current && view.type === "initial") {
|
||||||
setInitialPlan(initialPlanProp);
|
setInitialPlan(initialPlanProp);
|
||||||
setHasAutoStarted(true);
|
hasAutoStartedRef.current = true;
|
||||||
// Use a small timeout to allow state update to propagate before starting
|
// Use a small timeout to allow state update to propagate before starting
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
handleStartPlanningWithPlan(initialPlanProp);
|
handleStartPlanningWithPlan(initialPlanProp);
|
||||||
}, 0);
|
}, 0);
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}
|
}
|
||||||
}, [isOpen, initialPlanProp, hasAutoStarted, view.type]);
|
}, [isOpen, initialPlanProp, view.type]);
|
||||||
|
|
||||||
// Reset hasAutoStarted when modal closes
|
// Reset hasAutoStarted when modal closes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
setHasAutoStarted(false);
|
hasAutoStartedRef.current = false;
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user