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

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new 04aab1a51 fix(message): keep the history drawer search visible after 
reopening (#4564)
04aab1a51 is described below

commit 04aab1a5105c3f08d6295046fa8674011c1cc4a6
Author: 烤化の初雪 <[email protected]>
AuthorDate: Thu Sep 24 17:44:44 2026 +0800

    fix(message): keep the history drawer search visible after reopening (#4564)
    
    fix(message): keep the history drawer search visible after reopening
    
    The drawer's search input was uncontrolled while the applied keyword
    lived in component state, so the destroyed-on-close content came back
    with an empty field that contradicted the still-filtered tables. Drive
    the input from a draft that is restored from the applied search when
    the drawer opens, and commit it on submit.
    
    Fixes #4563.
---
 web/src/components/MessageQueryHistoryDrawer.tsx   | 18 +++++
 .../__tests__/MessageQueryHistoryDrawer.test.tsx   | 79 ++++++++++++++++++++++
 2 files changed, 97 insertions(+)

diff --git a/web/src/components/MessageQueryHistoryDrawer.tsx 
b/web/src/components/MessageQueryHistoryDrawer.tsx
index 8d83f0052..f96e70a77 100644
--- a/web/src/components/MessageQueryHistoryDrawer.tsx
+++ b/web/src/components/MessageQueryHistoryDrawer.tsx
@@ -41,6 +41,10 @@ const MessageQueryHistoryDrawer = ({
   const { t } = useLang();
   const [tab, setTab] = useState<'messages' | 'traces'>('messages');
   const [search, setSearch] = useState('');
+  // Draft value for the controlled search input. The drawer unmounts its 
content when
+  // closed (destroyOnHidden), so an uncontrolled input would come back empty 
while the
+  // applied search state keeps filtering the tables; the draft stays in sync 
instead.
+  const [searchDraft, setSearchDraft] = useState('');
   const [page, setPage] = useState(1);
   const [loading, setLoading] = useState(false);
   const [error, setError] = useState('');
@@ -99,6 +103,17 @@ const MessageQueryHistoryDrawer = ({
     };
   }, [load]);
 
+  // The drawer content is destroyed while closed; restore the visible search 
draft from
+  // the applied filter when the drawer opens so the input matches the 
filtered tables.
+  // Render-time adjustment (same pattern as the alerts domain switch), not an 
effect.
+  const [renderedOpen, setRenderedOpen] = useState(open);
+  if (open !== renderedOpen) {
+    setRenderedOpen(open);
+    if (open) {
+      setSearchDraft(search);
+    }
+  }
+
   const messageColumns: ColumnsType<MessageQueryHistory> = [
     {
       title: t('common.type'),
@@ -159,9 +174,12 @@ const MessageQueryHistoryDrawer = ({
       <Input.Search
         allowClear
         placeholder={t('messageHistory.searchPlaceholder')}
+        value={searchDraft}
+        onChange={(event) => setSearchDraft(event.target.value)}
         onSearch={(value) => {
           setPage(1);
           setSearch(value.trim());
+          setSearchDraft(value.trim());
         }}
         style={{ marginBottom: 12, width: 420 }}
       />
diff --git a/web/src/components/__tests__/MessageQueryHistoryDrawer.test.tsx 
b/web/src/components/__tests__/MessageQueryHistoryDrawer.test.tsx
index 22bd40759..098ba3c95 100644
--- a/web/src/components/__tests__/MessageQueryHistoryDrawer.test.tsx
+++ b/web/src/components/__tests__/MessageQueryHistoryDrawer.test.tsx
@@ -169,4 +169,83 @@ describe('MessageQueryHistoryDrawer', () => {
     ).toBeInTheDocument();
     expect(screen.queryByText('服务端查询历史')).not.toBeInTheDocument();
   });
+
+  it('keeps the applied search visible in the input after the drawer is closed 
and reopened', async () => {
+    localStorage.setItem(LANGUAGE_STORAGE_KEY, 'en');
+    const user = userEvent.setup();
+    const view = render(
+      <App>
+        <LangProvider>
+          <MessageQueryHistoryDrawer open clusterId="instance-a" 
onClose={vi.fn()} />
+        </LangProvider>
+      </App>,
+    );
+
+    expect(await screen.findByText('order-1')).toBeInTheDocument();
+    const searchInput = screen.getByPlaceholderText(
+      'Search Topic, trace Topic, Message ID, Key or operator',
+    );
+    await user.type(searchInput, 'order-1');
+    await user.keyboard('{Enter}');
+
+    await waitFor(() =>
+      expect(listMessageQueryHistory).toHaveBeenCalledWith(
+        expect.objectContaining({ search: 'order-1' }),
+      ),
+    );
+
+    view.rerender(
+      <App>
+        <LangProvider>
+          <MessageQueryHistoryDrawer open={false} clusterId="instance-a" 
onClose={vi.fn()} />
+        </LangProvider>
+      </App>,
+    );
+    view.rerender(
+      <App>
+        <LangProvider>
+          <MessageQueryHistoryDrawer open clusterId="instance-a" 
onClose={vi.fn()} />
+        </LangProvider>
+      </App>,
+    );
+
+    // The applied filter is still active after reopening, so the visible 
input must
+    // show it instead of an empty field that contradicts the filtered table 
below.
+    expect(await screen.findByText('order-1')).toBeInTheDocument();
+    expect(
+      await screen.findByPlaceholderText('Search Topic, trace Topic, Message 
ID, Key or operator'),
+    ).toHaveValue('order-1');
+  });
+
+  it('applies a cleared search field as an empty filter', async () => {
+    localStorage.setItem(LANGUAGE_STORAGE_KEY, 'en');
+    const user = userEvent.setup();
+    render(
+      <App>
+        <LangProvider>
+          <MessageQueryHistoryDrawer open clusterId="instance-a" 
onClose={vi.fn()} />
+        </LangProvider>
+      </App>,
+    );
+
+    expect(await screen.findByText('order-1')).toBeInTheDocument();
+    const searchInput = screen.getByPlaceholderText(
+      'Search Topic, trace Topic, Message ID, Key or operator',
+    );
+    await user.type(searchInput, 'order-1');
+    await user.keyboard('{Enter}');
+    await waitFor(() =>
+      expect(listMessageQueryHistory).toHaveBeenCalledWith(
+        expect.objectContaining({ search: 'order-1' }),
+      ),
+    );
+
+    await user.clear(searchInput);
+    await user.keyboard('{Enter}');
+    await waitFor(() =>
+      expect(listMessageQueryHistory).toHaveBeenLastCalledWith(
+        expect.objectContaining({ search: undefined }),
+      ),
+    );
+  });
 });

Reply via email to