From bc73c411f0bbf2b5e79c362ec78300ff5f08c520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Mon, 28 Sep 2026 03:37:32 +0000 Subject: [PATCH] feat(studio): let a host app mount the timeline toolbar, undo buttons, meters and clipboard (#4629) * feat(studio): let a host app mount the timeline toolbar, undo buttons, meters and clipboard * test(studio): lock in history button rules and trim duplicate export tests --- .../TimelineHistoryButtons.dom.test.tsx | 39 ++++++++- .../src/components/TimelineHistoryButtons.tsx | 40 ++++++---- .../src/components/TimelineToolbar.test.tsx | 10 ++- .../studio/src/components/TimelineToolbar.tsx | 18 +++-- .../components/nle/AudioMeterStrip.test.tsx | 29 ++++++- .../src/components/nle/AudioMeterStrip.tsx | 35 +++++--- .../src/components/pointerTargetSize.test.tsx | 2 +- .../src/hooks/useClipboard.source.test.ts | 11 +++ packages/studio/src/hooks/useClipboard.ts | 2 +- packages/studio/src/index.ts | 8 ++ .../studio/src/timelineChromeExports.test.tsx | 79 +++++++++++++++++++ 11 files changed, 235 insertions(+), 38 deletions(-) create mode 100644 packages/studio/src/timelineChromeExports.test.tsx diff --git a/packages/studio/src/components/TimelineHistoryButtons.dom.test.tsx b/packages/studio/src/components/TimelineHistoryButtons.dom.test.tsx index 230093002b..34346678a1 100644 --- a/packages/studio/src/components/TimelineHistoryButtons.dom.test.tsx +++ b/packages/studio/src/components/TimelineHistoryButtons.dom.test.tsx @@ -16,8 +16,10 @@ const handleUndo = vi.fn(); const handleRedo = vi.fn(); const trackStudioEvent = vi.fn(); +const studioShell = { editHistory, handleUndo, handleRedo }; +let shell: typeof studioShell | null = studioShell; vi.mock("../contexts/StudioContext", () => ({ - useStudioShellContext: () => ({ editHistory, handleUndo, handleRedo }), + useStudioShellContextOptional: () => shell, })); vi.mock("../utils/studioTelemetry", () => ({ trackStudioEvent })); @@ -31,6 +33,7 @@ beforeEach(() => { undoLabel: undefined, redoLabel: undefined, }); + shell = studioShell; vi.clearAllMocks(); }); @@ -115,3 +118,37 @@ it("classifies Undo and Redo for the hotkey filters at their new location (KTD13 expect(shouldIgnorePlaybackShortcutTarget(el), label).toBe(true); } }); + +it("takes a host's history props over the shell's", async () => { + editHistory.canUndo = true; + editHistory.redoLabel = "shell move"; + const onUndo = vi.fn(); + const onRedo = vi.fn(); + const host = mount( + , + ); + + expect(button(host, "Undo").disabled).toBe(true); + act(() => button(host, "Redo").focus()); + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 0)); + }); + expect(document.querySelector('[role="tooltip"]')?.textContent).toContain("Redo host trim"); + click(button(host, "Redo")); + + expect(onRedo).toHaveBeenCalledTimes(1); + expect(handleRedo).not.toHaveBeenCalled(); +}); + +it("keeps a button disabled when it has no handler, whatever canUndo/canRedo say", () => { + shell = null; + const host = mount(); + + for (const label of ["Undo", "Redo"]) expect(button(host, label).disabled).toBe(true); +}); diff --git a/packages/studio/src/components/TimelineHistoryButtons.tsx b/packages/studio/src/components/TimelineHistoryButtons.tsx index e779e8c75a..e9f75481b2 100644 --- a/packages/studio/src/components/TimelineHistoryButtons.tsx +++ b/packages/studio/src/components/TimelineHistoryButtons.tsx @@ -1,4 +1,4 @@ -import { useStudioShellContext } from "../contexts/StudioContext"; +import { useStudioShellContextOptional } from "../contexts/StudioContext"; import { RotateCcw, RotateCw } from "../icons/SystemIcons"; import { trackStudioEvent } from "../utils/studioTelemetry"; import { historyTooltipLabel } from "../utils/studioHelpers"; @@ -7,13 +7,14 @@ import { Tooltip } from "./ui"; interface HistoryButtonProps { action: "undo" | "redo"; - enabled: boolean; + can: boolean; label: string | undefined; - onClick: () => Promise | void; + onClick: (() => Promise | void) | undefined; } -function HistoryButton({ action, enabled, label, onClick }: HistoryButtonProps) { +function HistoryButton({ action, can, label, onClick }: HistoryButtonProps) { const Icon = action === "undo" ? RotateCcw : RotateCw; + const enabled = Boolean(onClick) && can; return (