mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(ui): preserve session picker on empty search blur (#87682)
Summary: - The PR changes the Control UI chat session picker blur handler to skip empty-query search application and adds a regression test that picker options remain clickable after an empty search blur. - PR surface: Source +4, Tests +52. Total +56 across 2 files. - Reproducibility: yes. The issue steps, before recording, and current-main source path all point to the same ... r clearing picker state before click delivery; I did not rerun a live browser repro in this read-only pass. Automerge notes: - PR branch already contained follow-up commit before automerge: fix(ui): preserve session picker on empty search blur Validation: - ClawSweeper review passed for headbb14687756. - Required merge gates passed before the squash merge. Prepared head SHA:bb14687756Review: https://github.com/openclaw/openclaw/pull/87682#issuecomment-4565441074 Co-authored-by: Ryan Weng <14496969+ryan4559@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com> Approved-by: takhoffman Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
This commit is contained in:
co-authored by
Ryan Weng
Claude Opus 4.6
clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
takhoffman
parent
0d66710539
commit
e9655b9fdc
@@ -594,7 +594,11 @@ function renderChatSessionPickerPopover(
|
||||
void applyChatSessionPickerSearch(state);
|
||||
}
|
||||
}}
|
||||
@blur=${() => void applyChatSessionPickerSearch(state)}
|
||||
@blur=${() => {
|
||||
if (normalizeOptionalString(state.chatSessionPickerQuery)) {
|
||||
void applyChatSessionPickerSearch(state);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
<button
|
||||
|
||||
@@ -1463,6 +1463,58 @@ describe("chat session controls", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps picker options clickable after blurring an empty search input", async () => {
|
||||
const { state } = createChatHeaderState();
|
||||
state.sessionsIncludeGlobal = false;
|
||||
state.sessionsIncludeUnknown = false;
|
||||
const rows: GatewaySessionRow[] = [
|
||||
{ key: "main", kind: "direct", label: "Main", updatedAt: 2 },
|
||||
{ key: "agent:main:work", kind: "direct", label: "Work", updatedAt: 1 },
|
||||
];
|
||||
state.sessionsResult = createSessionsResultFromRows(rows);
|
||||
const request = vi.fn((method: string) => {
|
||||
if (method === "sessions.list") {
|
||||
return Promise.resolve(createSessionsResultFromRows(rows));
|
||||
}
|
||||
throw new Error(`Unexpected request: ${method}`);
|
||||
});
|
||||
state.client = { request } as unknown as GatewayBrowserClient;
|
||||
const onSwitchSession = vi.fn();
|
||||
const container = document.createElement("div");
|
||||
render(renderChatSessionSelect(state, onSwitchSession), container);
|
||||
|
||||
container.querySelector<HTMLButtonElement>('button[data-chat-session-select="true"]')!.click();
|
||||
await vi.waitFor(() => expect(state.chatSessionPickerResult).not.toBeNull());
|
||||
render(renderChatSessionSelect(state, onSwitchSession), container);
|
||||
const pickerResultBefore = state.chatSessionPickerResult;
|
||||
const requestCountBefore = request.mock.calls.length;
|
||||
|
||||
const input = container.querySelector<HTMLInputElement>(
|
||||
'input[data-chat-session-picker-search="true"]',
|
||||
);
|
||||
expect(input!.value).toBe("");
|
||||
input!.dispatchEvent(new FocusEvent("blur", { bubbles: false }));
|
||||
render(renderChatSessionSelect(state, onSwitchSession), container);
|
||||
|
||||
expect(state.chatSessionPickerResult).toBe(pickerResultBefore);
|
||||
expect(state.chatSessionPickerAppliedQuery).toBe("");
|
||||
expect(state.chatSessionPickerOpen).toBe(true);
|
||||
expect(request).toHaveBeenCalledTimes(requestCountBefore);
|
||||
const options = container.querySelectorAll<HTMLButtonElement>(
|
||||
'button[data-chat-session-picker-option="true"]',
|
||||
);
|
||||
const target = [...options].find(
|
||||
(button) => button.dataset.sessionKey && button.dataset.sessionKey !== state.sessionKey,
|
||||
);
|
||||
if (!target?.dataset.sessionKey) {
|
||||
throw new Error("expected another session option");
|
||||
}
|
||||
const targetSessionKey = target.dataset.sessionKey;
|
||||
target.click();
|
||||
|
||||
expect(onSwitchSession).toHaveBeenCalledWith(state, targetSessionKey);
|
||||
});
|
||||
|
||||
it("clears applied chat session picker search when the input is cleared", async () => {
|
||||
const { state } = createChatHeaderState();
|
||||
state.sessionsIncludeGlobal = false;
|
||||
|
||||
Reference in New Issue
Block a user