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() }