Files
hyperframes/packages/studio/src/player/components/timelineTheme.test.ts
T
Miguel Ángel 037266e72b feat(studio): timeline revamp with active-clip highlighting and hide controls (#2017)
Timeline UI
- Highlight clips visible at the playhead in the primary color; others share one neutral color
- Minimalist rounded clips, single-color track rows, no gutter icons or superscript labels
- Per-track eye toggle and a per-element hide button in the design panel
- Ruler zoom fixes: sub-second tick intervals and correct label formatting at high zoom
- Sticky gutter so track controls stay visible while scrolling

WYSIWYG visibility (data-hidden)
- Runtime honors data-hidden (display:none), so hiding affects the render, not just the preview
- HTML stays the source of truth; hide state persists and round-trips on reload

Split several studio files to stay under the 600-line cap; pure relocations, no behavior change.
2026-07-07 04:26:56 -04:00

93 lines
2.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
getClipHandleOpacity,
getRenderedTimelineElement,
getTimelineTrackStyle,
} from "./timelineTheme";
import { getTrackStyle } from "./timelineIcons";
describe("getTimelineTrackStyle", () => {
it("uses one neutral clip style for every timeline tag", () => {
const expectedStyle = {
clip: "rgba(255,255,255,0.055)",
clipActive: "rgba(60,230,172,0.16)",
accent: "#3CE6AC",
label: "rgba(255,255,255,0.5)",
};
expect(getTimelineTrackStyle("video")).toEqual(expectedStyle);
expect(getTimelineTrackStyle("audio")).toEqual(expectedStyle);
expect(getTimelineTrackStyle("custom-tag")).toEqual(expectedStyle);
expect(getTimelineTrackStyle("video")).toEqual(getTimelineTrackStyle("audio"));
expect(getTimelineTrackStyle("video")).toEqual(getTimelineTrackStyle("custom-tag"));
});
});
describe("getTrackStyle", () => {
it("returns the timeline style only and preserves the empty tag fallback", () => {
const style = getTrackStyle("");
expect(style).toEqual(getTimelineTrackStyle("div"));
expect(Object.keys(style)).not.toContain("icon");
expect(Object.keys(style)).not.toContain("iconBackground");
});
});
describe("getClipHandleOpacity", () => {
it("hides handles at rest", () => {
expect(getClipHandleOpacity({ isHovered: false, isSelected: false, isDragging: false })).toBe(
0,
);
});
it("prioritizes dragging over hover and selection", () => {
expect(getClipHandleOpacity({ isHovered: true, isSelected: true, isDragging: true })).toBe(
0.95,
);
});
});
describe("getRenderedTimelineElement", () => {
it("keeps non-dragged clips unchanged", () => {
const element = { id: "a", tag: "div", start: 1, duration: 2, track: 0 };
expect(
getRenderedTimelineElement({
element,
draggedElementId: "b",
previewStart: 2,
previewTrack: 1,
}),
).toEqual(element);
});
it("moves the actual dragged clip to the preview position", () => {
const element = { id: "a", tag: "div", start: 1, duration: 2, track: 0 };
expect(
getRenderedTimelineElement({
element,
draggedElementId: "a",
previewStart: 2.4,
previewTrack: 3,
}),
).toEqual({ ...element, start: 2.4, track: 3 });
});
it("uses key before id when matching the dragged clip", () => {
const element = {
id: "Card",
key: "index.html:.card:1",
tag: "div",
start: 1,
duration: 2,
track: 0,
};
expect(
getRenderedTimelineElement({
element,
draggedElementId: "index.html:.card:1",
previewStart: 2.4,
previewTrack: 3,
}),
).toEqual({ ...element, start: 2.4, track: 3 });
});
});