mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 04:15:48 -06:00
fix(ui): make Settings Escape contextual (#128099)
* fix(ui): make Settings Escape contextual Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com> * fix(ui): scope Settings Escape to search --------- Co-authored-by: Peter Steinberger <steipete@gmail.com> Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
This commit is contained in:
@@ -20,6 +20,12 @@ type ShellNavigationState = {
|
||||
updated: () => void;
|
||||
};
|
||||
|
||||
type ShellSettingsEscapeState = ShellKeyboardState & {
|
||||
lastWorkspaceLocation: { routeId: "usage"; pathname: string; search: string };
|
||||
navDrawerOpen: boolean;
|
||||
routeState: { routeId: "appearance" };
|
||||
};
|
||||
|
||||
type TestWebKitWindow = Window & {
|
||||
webkit?: {
|
||||
messageHandlers: {
|
||||
@@ -107,6 +113,41 @@ describe("OpenClaw native shell", () => {
|
||||
expect(navigate).toHaveBeenCalledWith("appearance", undefined);
|
||||
});
|
||||
|
||||
it("keeps the raw config editor unchanged when Escape is pressed", () => {
|
||||
const navigate = vi.fn();
|
||||
const shell = document.createElement(
|
||||
"openclaw-app-shell",
|
||||
) as unknown as ShellSettingsEscapeState;
|
||||
shell.runtime = {
|
||||
context: {
|
||||
navigate,
|
||||
overlays: { snapshot: { devicePairSetupOpen: false } },
|
||||
} as unknown as ApplicationContext,
|
||||
};
|
||||
shell.lastWorkspaceLocation = { routeId: "usage", pathname: "/usage", search: "" };
|
||||
shell.navDrawerOpen = false;
|
||||
shell.routeState = { routeId: "appearance" };
|
||||
const rawField = document.body.appendChild(document.createElement("label"));
|
||||
rawField.className = "config-raw-field";
|
||||
const rawEditor = rawField.appendChild(document.createElement("textarea"));
|
||||
rawEditor.value = '{ "gateway": { "port": 18789 } }';
|
||||
rawEditor.focus();
|
||||
const onInput = vi.fn();
|
||||
rawEditor.addEventListener("input", onInput);
|
||||
rawEditor.addEventListener("keydown", (event) => shell.handleDocumentKeydown(event));
|
||||
|
||||
try {
|
||||
const event = new KeyboardEvent("keydown", { key: "Escape", cancelable: true });
|
||||
rawEditor.dispatchEvent(event);
|
||||
|
||||
expect(rawEditor.value).toBe('{ "gateway": { "port": 18789 } }');
|
||||
expect(onInput).not.toHaveBeenCalled();
|
||||
expect(navigate).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
rawField.remove();
|
||||
}
|
||||
});
|
||||
|
||||
it("toggles the navigation sidebar when the native macOS titlebar button fires", () => {
|
||||
const snapshot = { navCollapsed: false };
|
||||
const update = vi.fn((next: { navCollapsed: boolean }) => {
|
||||
|
||||
@@ -440,6 +440,54 @@ describe("settings sidebar search", () => {
|
||||
expect(onNavigate).toHaveBeenCalledWith("channels");
|
||||
});
|
||||
|
||||
it("clears a focused search before Escape exits Settings", () => {
|
||||
let searchQuery = "gateway";
|
||||
const onExit = vi.fn();
|
||||
const rerender = () => {
|
||||
render(
|
||||
renderSettingsSidebar({
|
||||
basePath: "",
|
||||
activeRouteId: "appearance",
|
||||
offline: false,
|
||||
lastError: null,
|
||||
gatewayVersion: "",
|
||||
updateAvailable: null,
|
||||
updateBusy: false,
|
||||
onUpdate: vi.fn(),
|
||||
...inactiveRefresh,
|
||||
searchQuery,
|
||||
onExit,
|
||||
onRetryConnect: vi.fn(),
|
||||
onNavigate: vi.fn(),
|
||||
onSearchQueryChange: (nextQuery) => {
|
||||
searchQuery = nextQuery;
|
||||
rerender();
|
||||
},
|
||||
preloadTimers: new Map(),
|
||||
saveIndicator: saveIndicator(),
|
||||
}),
|
||||
container,
|
||||
);
|
||||
};
|
||||
|
||||
rerender();
|
||||
const input = container.querySelector<HTMLInputElement>(".settings-sidebar__search-input");
|
||||
expect(input).not.toBeNull();
|
||||
input?.focus();
|
||||
|
||||
input?.dispatchEvent(
|
||||
new KeyboardEvent("keydown", { key: "Escape", bubbles: true, cancelable: true }),
|
||||
);
|
||||
expect(searchQuery).toBe("");
|
||||
expect(document.activeElement).toBe(input);
|
||||
expect(onExit).not.toHaveBeenCalled();
|
||||
|
||||
input?.dispatchEvent(
|
||||
new KeyboardEvent("keydown", { key: "Escape", bubbles: true, cancelable: true }),
|
||||
);
|
||||
expect(onExit).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("renders refreshed settings route titles from the active locale", async () => {
|
||||
i18n.registerTranslation("pt-BR", {
|
||||
routeTitles: {
|
||||
|
||||
@@ -272,11 +272,15 @@ export function renderSettingsSidebar(props: SettingsSidebarProps) {
|
||||
@input=${(event: Event) =>
|
||||
props.onSearchQueryChange((event.currentTarget as HTMLInputElement).value)}
|
||||
@keydown=${(event: KeyboardEvent) => {
|
||||
if (event.key !== "Escape" || !props.searchQuery) {
|
||||
if (event.key !== "Escape") {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
props.onSearchQueryChange("");
|
||||
if (props.searchQuery) {
|
||||
props.onSearchQueryChange("");
|
||||
return;
|
||||
}
|
||||
props.onExit();
|
||||
}}
|
||||
/>
|
||||
${props.searchQuery
|
||||
|
||||
Reference in New Issue
Block a user