mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-26 10:13:27 +08:00
fix(ui): keep keyboard focus when collapsing standalone questions (#181296)
Preserve keyboard focus on Expand when a standalone question collapses, retaining its answer draft and Chat composer focus behavior. Reviewed-by: @shakkernerd
This commit is contained in:
1 parent
f5c8e0dbeb
commit
d3359fe448
2 files changed
+37
-1
No files matched your search
@@ -1,4 +1,12 @@
|
||||
import { action, createMemo, createOptimistic, createSignal, Show, untrack } from "solid-js";
|
||||
import {
|
||||
action,
|
||||
createMemo,
|
||||
createOptimistic,
|
||||
createSignal,
|
||||
onSettled,
|
||||
Show,
|
||||
untrack,
|
||||
} from "solid-js";
|
||||
import type { QuestionDraft } from "../../../app/question-prompt.ts";
|
||||
import { Icon } from "../../../components/solid/icon.tsx";
|
||||
import { registerChatSessionEnglish } from "../../../i18n/locales/en-chat-session.ts";
|
||||
@@ -35,6 +43,7 @@ function QuestionPanelRequest(props: { panel: QuestionPanelProps }) {
|
||||
const [pendingKind, setPendingKind] = createOptimistic<"submit" | "skip" | null>(null);
|
||||
let resolving = false;
|
||||
let panel: HTMLElement | undefined;
|
||||
let expandButton: HTMLButtonElement | undefined;
|
||||
let answerControl: HTMLInputElement | HTMLTextAreaElement | undefined;
|
||||
let hostsInput: HTMLInputElement | undefined;
|
||||
const options = new Set<HTMLButtonElement>();
|
||||
@@ -73,6 +82,10 @@ function QuestionPanelRequest(props: { panel: QuestionPanelProps }) {
|
||||
props.panel.onCollapsedChange(value);
|
||||
} else {
|
||||
setUncontrolledCollapsed(value);
|
||||
// Controlled callers own their destination (for example, the chat composer).
|
||||
if (value) {
|
||||
onSettled(() => expandButton?.focus({ preventScroll: true }));
|
||||
}
|
||||
}
|
||||
};
|
||||
const updateDraft = (item: QuestionPanelQuestion, value: QuestionDraft) => {
|
||||
@@ -316,6 +329,7 @@ function QuestionPanelRequest(props: { panel: QuestionPanelProps }) {
|
||||
aria-label={model().title}
|
||||
>
|
||||
<button
|
||||
ref={(element) => (expandButton = element)}
|
||||
class="chat-question-panel__collapsed-button"
|
||||
type="button"
|
||||
onClick={() => setCollapsed(false)}
|
||||
|
||||
@@ -76,6 +76,28 @@ it.each(["question", "outside"] as const)("restores only question focus (%s)", a
|
||||
expect(request).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("keeps focus on the collapsed question so it can be reopened", async () => {
|
||||
const { page } = await mount(vi.fn().mockResolvedValue({ question: question() }));
|
||||
await questionPanelIn(page);
|
||||
const input = page.querySelector("textarea")!;
|
||||
input.value = "The design team";
|
||||
input.dispatchEvent(new InputEvent("input", { bubbles: true }));
|
||||
const collapse = page.querySelector<HTMLButtonElement>(".chat-question-panel__collapse")!;
|
||||
collapse.focus();
|
||||
collapse.click();
|
||||
await waitForSolid(() => {
|
||||
const expand = page.querySelector(".chat-question-panel__collapsed-button");
|
||||
expect(expand).not.toBeNull();
|
||||
expect(document.activeElement).toBe(expand);
|
||||
});
|
||||
|
||||
page.querySelector<HTMLButtonElement>(".chat-question-panel__collapsed-button")!.click();
|
||||
await waitForSolid(() => {
|
||||
expect(document.activeElement).toBe(page.querySelector(".chat-question-panel"));
|
||||
expect(page.querySelector("textarea")?.value).toBe("The design team");
|
||||
});
|
||||
});
|
||||
|
||||
it("recovers a failed reconnect lookup without losing the typed answer", async () => {
|
||||
const request = vi
|
||||
.fn()
|
||||
|
||||
Reference in new issue
Block a user