feat(FN-3343): add Escape key keyboard handling to modals

This merge delivers v0.17.2 and ships FN-3343 keyboard handling across four dashboard modals (NewTaskModal, SubtaskBreakdownModal, MilestoneSliceInterviewModal, SettingsModal) with mobile CSS overrides, plus keyboard style regression tests for each modal and a minor test alignment for the onboarding

Fusion-Task-Id: FN-3343
This commit is contained in:
Fusion
2026-05-04 01:37:33 -07:00
committed by gsxdsm
parent 3092f89379
commit 2d72eddb9c
10 changed files with 216 additions and 7 deletions

View File

@@ -1,4 +1,4 @@
import { useState, useCallback, useEffect, useMemo, useRef } from "react";
import { useState, useCallback, useEffect, useMemo, useRef, type CSSProperties } from "react";
import type { PlanningQuestion } from "@fusion/core";
import { getErrorMessage } from "@fusion/core";
import {
@@ -29,7 +29,9 @@ import {
import { ConversationHistory } from "./ConversationHistory";
import { useSessionLock } from "../hooks/useSessionLock";
import { useAiSessionSync } from "../hooks/useAiSessionSync";
import { useMobileKeyboard } from "../hooks/useMobileKeyboard";
import { useMobileScrollLock } from "../hooks/useMobileScrollLock";
import { useViewportMode } from "../hooks/useViewportMode";
import { getSessionTabId } from "../utils/getSessionTabId";
interface MilestoneSliceInterviewModalProps {
@@ -74,7 +76,18 @@ export function MilestoneSliceInterviewModal({
projectId,
resumeSessionId,
}: MilestoneSliceInterviewModalProps) {
const viewportMode = useViewportMode();
useMobileScrollLock(isOpen);
const { keyboardOverlap, viewportHeight, viewportOffsetTop, keyboardOpen } = useMobileKeyboard({
enabled: viewportMode === "mobile",
});
const keyboardStyle: CSSProperties = keyboardOpen
? ({
"--keyboard-overlap": `${keyboardOverlap}px`,
"--vv-offset-top": `${viewportOffsetTop}px`,
...(viewportHeight !== null ? { "--vv-height": `${viewportHeight}px` } : {}),
} as CSSProperties)
: {};
const [view, setView] = useState<ViewState>({ type: "initial" });
const [error, setError] = useState<string | null>(null);
const [responseHistory, setResponseHistory] = useState<QuestionResponse[]>([]);
@@ -451,7 +464,7 @@ export function MilestoneSliceInterviewModal({
aria-modal="true"
data-testid="milestone-slice-interview-modal"
>
<div className="modal modal-lg planning-modal">
<div className="modal modal-lg planning-modal" style={keyboardStyle}>
<div className="modal-header">
<div className="detail-title-row">
<Sparkles size={20} className="icon-triage" />