test(chat): enable virtual keyboard overlay experiment
This commit is contained in:
@@ -5,6 +5,7 @@ import { useChatDispatch } from "@/stores/chat/chat-context";
|
|||||||
import { Logger } from "@/utils/logger";
|
import { Logger } from "@/utils/logger";
|
||||||
|
|
||||||
import { useChatKeyboardDiagnostics } from "../hooks/use-chat-keyboard-diagnostics";
|
import { useChatKeyboardDiagnostics } from "../hooks/use-chat-keyboard-diagnostics";
|
||||||
|
import { useChatVirtualKeyboardOverlayExperiment } from "../hooks/use-chat-virtual-keyboard-overlay-experiment";
|
||||||
import { ChatInputTextField } from "./chat-input-text-field";
|
import { ChatInputTextField } from "./chat-input-text-field";
|
||||||
import { ChatSendButton } from "./chat-send-button";
|
import { ChatSendButton } from "./chat-send-button";
|
||||||
import styles from "./chat-input-bar.module.css";
|
import styles from "./chat-input-bar.module.css";
|
||||||
@@ -26,6 +27,7 @@ export function ChatInputBar({ disabled = false }: ChatInputBarProps) {
|
|||||||
|
|
||||||
const hasContent = input.trim().length > 0;
|
const hasContent = input.trim().length > 0;
|
||||||
|
|
||||||
|
useChatVirtualKeyboardOverlayExperiment();
|
||||||
useChatKeyboardDiagnostics({ containerRef: barRef });
|
useChatKeyboardDiagnostics({ containerRef: barRef });
|
||||||
|
|
||||||
const handleInputChange = (value: string) => {
|
const handleInputChange = (value: string) => {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class MockVisualViewport extends EventTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MockVirtualKeyboard extends EventTarget {
|
class MockVirtualKeyboard extends EventTarget {
|
||||||
|
overlaysContent = false;
|
||||||
boundingRect = {
|
boundingRect = {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 460,
|
y: 460,
|
||||||
@@ -96,6 +97,7 @@ describe("chat keyboard diagnostics", () => {
|
|||||||
visualViewportSupported: true,
|
visualViewportSupported: true,
|
||||||
visualHeight: 460,
|
visualHeight: 460,
|
||||||
virtualKeyboardSupported: true,
|
virtualKeyboardSupported: true,
|
||||||
|
virtualKeyboardOverlaysContent: false,
|
||||||
virtualKeyboardRect: { height: 300 },
|
virtualKeyboardRect: { height: 300 },
|
||||||
activeElement: "TEXTAREA",
|
activeElement: "TEXTAREA",
|
||||||
activeFocused: true,
|
activeFocused: true,
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/* @vitest-environment jsdom */
|
||||||
|
|
||||||
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
import {
|
||||||
|
enableChatVirtualKeyboardOverlayExperiment,
|
||||||
|
isChatVirtualKeyboardOverlayExperimentEnabled,
|
||||||
|
} from "../use-chat-virtual-keyboard-overlay-experiment";
|
||||||
|
|
||||||
|
describe("Virtual Keyboard Overlay experiment", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("enables only for the explicit experiment parameter", () => {
|
||||||
|
expect(
|
||||||
|
isChatVirtualKeyboardOverlayExperimentEnabled(
|
||||||
|
"?keyboard_debug=1&virtual_keyboard_overlay=1",
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
isChatVirtualKeyboardOverlayExperimentEnabled(
|
||||||
|
"?keyboard_debug=1&virtual_keyboard_overlay=0",
|
||||||
|
),
|
||||||
|
).toBe(false);
|
||||||
|
expect(isChatVirtualKeyboardOverlayExperimentEnabled("")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("enables overlay content and restores the previous value", () => {
|
||||||
|
const virtualKeyboard = { overlaysContent: false };
|
||||||
|
Object.defineProperty(navigator, "virtualKeyboard", {
|
||||||
|
configurable: true,
|
||||||
|
value: virtualKeyboard,
|
||||||
|
});
|
||||||
|
vi.spyOn(window.console, "info").mockImplementation(() => {});
|
||||||
|
|
||||||
|
const cleanup = enableChatVirtualKeyboardOverlayExperiment();
|
||||||
|
|
||||||
|
expect(virtualKeyboard.overlaysContent).toBe(true);
|
||||||
|
cleanup();
|
||||||
|
expect(virtualKeyboard.overlaysContent).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -8,6 +8,7 @@ import { PlatformDetector } from "@/utils/platform-detect";
|
|||||||
const CHAT_KEYBOARD_DEBUG_PARAM = "keyboard_debug";
|
const CHAT_KEYBOARD_DEBUG_PARAM = "keyboard_debug";
|
||||||
|
|
||||||
interface VirtualKeyboardLike extends EventTarget {
|
interface VirtualKeyboardLike extends EventTarget {
|
||||||
|
readonly overlaysContent?: boolean;
|
||||||
readonly boundingRect?: Pick<
|
readonly boundingRect?: Pick<
|
||||||
DOMRectReadOnly,
|
DOMRectReadOnly,
|
||||||
"height" | "width" | "x" | "y"
|
"height" | "width" | "x" | "y"
|
||||||
@@ -31,6 +32,7 @@ export interface ChatKeyboardDebugSnapshot {
|
|||||||
visualOffsetLeft: number | null;
|
visualOffsetLeft: number | null;
|
||||||
visualScale: number | null;
|
visualScale: number | null;
|
||||||
virtualKeyboardSupported: boolean;
|
virtualKeyboardSupported: boolean;
|
||||||
|
virtualKeyboardOverlaysContent: boolean | null;
|
||||||
virtualKeyboardRect: {
|
virtualKeyboardRect: {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
@@ -170,6 +172,8 @@ function createChatKeyboardDebugSnapshot(
|
|||||||
visualOffsetLeft: visualViewport?.offsetLeft ?? null,
|
visualOffsetLeft: visualViewport?.offsetLeft ?? null,
|
||||||
visualScale: visualViewport?.scale ?? null,
|
visualScale: visualViewport?.scale ?? null,
|
||||||
virtualKeyboardSupported: getVirtualKeyboard() != null,
|
virtualKeyboardSupported: getVirtualKeyboard() != null,
|
||||||
|
virtualKeyboardOverlaysContent:
|
||||||
|
getVirtualKeyboard()?.overlaysContent ?? null,
|
||||||
virtualKeyboardRect: virtualKeyboardRect
|
virtualKeyboardRect: virtualKeyboardRect
|
||||||
? {
|
? {
|
||||||
x: virtualKeyboardRect.x,
|
x: virtualKeyboardRect.x,
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
const CHAT_VIRTUAL_KEYBOARD_OVERLAY_PARAM = "virtual_keyboard_overlay";
|
||||||
|
|
||||||
|
interface VirtualKeyboardOverlayLike {
|
||||||
|
overlaysContent: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NavigatorWithVirtualKeyboard extends Navigator {
|
||||||
|
readonly virtualKeyboard?: VirtualKeyboardOverlayLike;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useChatVirtualKeyboardOverlayExperiment(): void {
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isChatVirtualKeyboardOverlayExperimentEnabled(window.location.search)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return enableChatVirtualKeyboardOverlayExperiment();
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isChatVirtualKeyboardOverlayExperimentEnabled(
|
||||||
|
search: string,
|
||||||
|
): boolean {
|
||||||
|
return (
|
||||||
|
new URLSearchParams(search).get(CHAT_VIRTUAL_KEYBOARD_OVERLAY_PARAM) ===
|
||||||
|
"1"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enableChatVirtualKeyboardOverlayExperiment(): () => void {
|
||||||
|
const virtualKeyboard = getVirtualKeyboard();
|
||||||
|
if (!virtualKeyboard) {
|
||||||
|
window.console.info(
|
||||||
|
"[keyboard-debug] Virtual Keyboard Overlay API is unavailable",
|
||||||
|
);
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousValue = virtualKeyboard.overlaysContent;
|
||||||
|
try {
|
||||||
|
virtualKeyboard.overlaysContent = true;
|
||||||
|
window.console.info("[keyboard-debug] Virtual Keyboard Overlay enabled", {
|
||||||
|
previousValue,
|
||||||
|
currentValue: virtualKeyboard.overlaysContent,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
window.console.info(
|
||||||
|
"[keyboard-debug] Virtual Keyboard Overlay failed",
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
return () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
try {
|
||||||
|
virtualKeyboard.overlaysContent = previousValue;
|
||||||
|
} catch {
|
||||||
|
// The WebView may invalidate the API object while the route is closing.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVirtualKeyboard(): VirtualKeyboardOverlayLike | null {
|
||||||
|
const virtualKeyboard = (
|
||||||
|
navigator as NavigatorWithVirtualKeyboard
|
||||||
|
).virtualKeyboard;
|
||||||
|
return virtualKeyboard && typeof virtualKeyboard.overlaysContent === "boolean"
|
||||||
|
? virtualKeyboard
|
||||||
|
: null;
|
||||||
|
}
|
||||||
@@ -1,12 +1,7 @@
|
|||||||
import type { Viewport } from "next";
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import { ChatRouteProviders } from "@/providers/chat-route-providers";
|
import { ChatRouteProviders } from "@/providers/chat-route-providers";
|
||||||
|
|
||||||
export const viewport: Viewport = {
|
|
||||||
interactiveWidget: "resizes-content",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function ChatLayout({ children }: { children: ReactNode }) {
|
export default function ChatLayout({ children }: { children: ReactNode }) {
|
||||||
return <ChatRouteProviders>{children}</ChatRouteProviders>;
|
return <ChatRouteProviders>{children}</ChatRouteProviders>;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user