From e36634786efc3dcb172a33dad9f5b3ec840c53fb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 3 Aug 2026 00:28:41 +0000 Subject: [PATCH] fix(campaign): clear RelationChipCell blur timeout on unmount Prevents setState after jsdom teardown (window is not defined) that failed CI unit on leadershipStateDeputyRelationCell. --- .../campaign/shared/RelationChipCell.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/components/campaign/shared/RelationChipCell.tsx b/src/components/campaign/shared/RelationChipCell.tsx index cb8aa3f2..59b426ea 100644 --- a/src/components/campaign/shared/RelationChipCell.tsx +++ b/src/components/campaign/shared/RelationChipCell.tsx @@ -168,6 +168,8 @@ export const RelationChipCell = ({ const rootRef = useRef(null) const inputRef = useRef(null) const chipRowRef = useRef(null) + /** Blur debounce — cleared on unmount so setState cannot fire after jsdom teardown (CI flake). */ + const blurCloseTimeoutRef = useRef(null) const baseId = useId() const listboxId = (surface: SuggestionSurface) => `${baseId}-${surface}-listbox` const optionId = useCallback( @@ -175,6 +177,15 @@ export const RelationChipCell = ({ [baseId], ) + useEffect(() => { + return () => { + if (blurCloseTimeoutRef.current != null) { + window.clearTimeout(blurCloseTimeoutRef.current) + blurCloseTimeoutRef.current = null + } + } + }, []) + useEffect(() => { setOptimistic((current) => { if (!current) return null @@ -685,7 +696,11 @@ export const RelationChipCell = ({ }} onFocus={() => setOpen(true)} onBlur={() => { - window.setTimeout(() => { + if (blurCloseTimeoutRef.current != null) { + window.clearTimeout(blurCloseTimeoutRef.current) + } + blurCloseTimeoutRef.current = window.setTimeout(() => { + blurCloseTimeoutRef.current = null if (!rootRef.current?.contains(document.activeElement)) { closeSuggestions() }