This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new d00b3821f15 UI: fix Searchbar input rewind  (#66284)
d00b3821f15 is described below

commit d00b3821f1597787f99d41be989b9902891aab9a
Author: hojeong park <[email protected]>
AuthorDate: Tue May 5 00:00:33 2026 +0900

    UI: fix Searchbar input rewind  (#66284)
    
    * UI: Fix SearchBar input rewind during fast typing
    
    * UI: Add SearchBar regression test for input rewind during fast typing
---
 .../airflow/ui/src/components/SearchBar.test.tsx   | 27 ++++++++++++++++++++++
 .../src/airflow/ui/src/components/SearchBar.tsx    | 12 ++++++++--
 2 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/airflow-core/src/airflow/ui/src/components/SearchBar.test.tsx 
b/airflow-core/src/airflow/ui/src/components/SearchBar.test.tsx
index 9d981a483a0..dba492e5f8a 100644
--- a/airflow-core/src/airflow/ui/src/components/SearchBar.test.tsx
+++ b/airflow-core/src/airflow/ui/src/components/SearchBar.test.tsx
@@ -116,6 +116,33 @@ describe("Test SearchBar", () => {
     expect((input as HTMLInputElement).value).toBe("user-typing");
   });
 
+  it("does not rewind in-flight typing when defaultValue echoes a stale 
value", () => {
+    vi.useFakeTimers();
+
+    const onChange = vi.fn();
+    const { rerender } = render(<SearchBar defaultValue="" onChange={onChange} 
placeholder="Search Dags" />, {
+      wrapper: Wrapper,
+    });
+    const input = screen.getByTestId("search-dags");
+
+    // Type "ab" and let the debounce flush so the parent receives 
onChange("ab").
+    fireEvent.change(input, { target: { value: "ab" } });
+    act(() => {
+      vi.advanceTimersByTime(200);
+    });
+    expect(onChange).toHaveBeenLastCalledWith("ab");
+
+    // The user keeps typing before the parent's URL state has propagated back.
+    fireEvent.change(input, { target: { value: "abc" } });
+    expect((input as HTMLInputElement).value).toBe("abc");
+
+    // The lagging echo of our own send arrives: parent rerenders with the
+    // previously-sent value. The in-flight character must not be clobbered.
+    rerender(<SearchBar defaultValue="ab" onChange={onChange} 
placeholder="Search Dags" />);
+
+    expect((input as HTMLInputElement).value).toBe("abc");
+  });
+
   it("does not render advanced toggle by default", () => {
     render(<SearchBar defaultValue="" onChange={vi.fn()} placeholder="Search" 
/>, {
       wrapper: Wrapper,
diff --git a/airflow-core/src/airflow/ui/src/components/SearchBar.tsx 
b/airflow-core/src/airflow/ui/src/components/SearchBar.tsx
index 115ade95b10..938d8246173 100644
--- a/airflow-core/src/airflow/ui/src/components/SearchBar.tsx
+++ b/airflow-core/src/airflow/ui/src/components/SearchBar.tsx
@@ -46,14 +46,21 @@ export const SearchBar = ({
   placeholder,
   ...props
 }: Props) => {
-  const handleSearchChange = useDebouncedCallback((val: string) => 
onChange(val), debounceDelay);
+  const lastSentValue = useRef(defaultValue);
+  const handleSearchChange = useDebouncedCallback((val: string) => {
+    lastSentValue.current = val;
+    onChange(val);
+  }, debounceDelay);
   const searchRef = useRef<HTMLInputElement>(null);
   const [value, setValue] = useState(defaultValue);
   const metaKey = getMetaKey();
   const { t: translate } = useTranslation(["dags"]);
 
   useEffect(() => {
-    setValue(defaultValue);
+    if (defaultValue !== lastSentValue.current) {
+      setValue(defaultValue);
+      lastSentValue.current = defaultValue;
+    }
   }, [defaultValue]);
 
   const onSearchChange = (event: ChangeEvent<HTMLInputElement>) => {
@@ -62,6 +69,7 @@ export const SearchBar = ({
   };
   const clearSearch = () => {
     handleSearchChange.cancel();
+    lastSentValue.current = "";
     setValue("");
     onChange("");
   };

Reply via email to