mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
feat(studio): add flat footer (ask agent + record) for the inspector
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import React, { act } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { PropertyPanelFlatFooter } from "./PropertyPanelFlatFooter";
|
||||
|
||||
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
afterEach(() => {
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
function renderFooter(overrides: Partial<Parameters<typeof PropertyPanelFlatFooter>[0]> = {}) {
|
||||
const host = document.createElement("div");
|
||||
document.body.append(host);
|
||||
const root = createRoot(host);
|
||||
act(() => {
|
||||
root.render(<PropertyPanelFlatFooter {...overrides} />);
|
||||
});
|
||||
return { host, root };
|
||||
}
|
||||
|
||||
describe("PropertyPanelFlatFooter", () => {
|
||||
it("renders the ask-agent affordance and fires onAskAgent on click", () => {
|
||||
const onAskAgent = vi.fn();
|
||||
const { host, root } = renderFooter({ onAskAgent });
|
||||
expect(host.textContent).toContain("Ask agent about this element");
|
||||
const askButton = host.querySelector<HTMLButtonElement>('[data-flat-footer-ask="true"]');
|
||||
act(() => askButton?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
||||
expect(onAskAgent).toHaveBeenCalledTimes(1);
|
||||
act(() => root.unmount());
|
||||
});
|
||||
|
||||
it("shows the idle record affordance and toggles recording on click", () => {
|
||||
const onToggleRecording = vi.fn();
|
||||
const { host, root } = renderFooter({ recordingState: "idle", onToggleRecording });
|
||||
const recordButton = host.querySelector<HTMLButtonElement>('[data-flat-footer-record="true"]');
|
||||
expect(recordButton?.title).toBe("Record gesture (R)");
|
||||
act(() => recordButton?.dispatchEvent(new MouseEvent("click", { bubbles: true })));
|
||||
expect(onToggleRecording).toHaveBeenCalledTimes(1);
|
||||
act(() => root.unmount());
|
||||
});
|
||||
|
||||
it("shows the recording duration while recording", () => {
|
||||
const { host, root } = renderFooter({
|
||||
recordingState: "recording",
|
||||
recordingDuration: 2.4,
|
||||
onToggleRecording: vi.fn(),
|
||||
});
|
||||
const recordButton = host.querySelector<HTMLButtonElement>('[data-flat-footer-record="true"]');
|
||||
expect(recordButton?.title).toBe("Stop recording 2.4s");
|
||||
act(() => root.unmount());
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
export function PropertyPanelFlatFooter({
|
||||
onAskAgent,
|
||||
recordingState,
|
||||
recordingDuration,
|
||||
onToggleRecording,
|
||||
}: {
|
||||
onAskAgent?: () => void;
|
||||
recordingState?: "idle" | "recording" | "preview";
|
||||
recordingDuration?: number;
|
||||
onToggleRecording?: () => void;
|
||||
}) {
|
||||
const recording = recordingState === "recording";
|
||||
const recordTitle = recording
|
||||
? `Stop recording ${(recordingDuration ?? 0).toFixed(1)}s`
|
||||
: "Record gesture (R)";
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between border-t border-panel-hairline px-4 py-[11px]">
|
||||
<button
|
||||
type="button"
|
||||
data-flat-footer-ask="true"
|
||||
onClick={onAskAgent}
|
||||
disabled={!onAskAgent}
|
||||
className="flex items-center gap-[7px] text-[11px] font-medium text-panel-text-2 disabled:cursor-not-allowed"
|
||||
>
|
||||
<svg
|
||||
width="13"
|
||||
height="13"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
className="text-panel-accent"
|
||||
>
|
||||
<path d="M8 1l1.4 4.6L14 7l-4.6 1.4L8 13l-1.4-4.6L2 7l4.6-1.4z" />
|
||||
</svg>
|
||||
Ask agent about this element
|
||||
</button>
|
||||
{onToggleRecording && (
|
||||
<button
|
||||
type="button"
|
||||
data-flat-footer-record="true"
|
||||
aria-label={recordTitle}
|
||||
title={recordTitle}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onClick={onToggleRecording}
|
||||
className={recording ? "text-panel-danger animate-pulse" : "text-panel-danger"}
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor">
|
||||
{recording ? (
|
||||
<rect x="2" y="2" width="10" height="10" rx="1.5" />
|
||||
) : (
|
||||
<circle cx="7" cy="7" r="6" />
|
||||
)}
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user