mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix(studio): grow the ease target only where the segment has room
The 24x24 WCAG 2.5.8 overlay sits on a wrapper that outranks the diamonds, so on a segment narrower than 24px it overhung them and won their hit test at fit zoom. Gate the overlay on the clear span between the two diamonds and let the button keep its 16x16 box below that. Also renames the pointer target suite to say it asserts the classes that produce the size, not the measured geometry, which happy-dom cannot see.
This commit is contained in:
@@ -33,7 +33,7 @@ function mount(element: React.ReactNode) {
|
||||
return host;
|
||||
}
|
||||
|
||||
describe("pointer target size (WCAG 2.5.8)", () => {
|
||||
describe("pointer target sizing classes (proxy for WCAG 2.5.8, not a geometry check)", () => {
|
||||
it("gives the timeline zoom slider a 24px box without changing its 2px track or 10px thumb", () => {
|
||||
const host = mount(<TimelineToolbar />);
|
||||
const slider = host.querySelector<HTMLInputElement>('input[aria-label="Timeline zoom"]');
|
||||
|
||||
@@ -641,7 +641,7 @@ describe("TimelineClipDiamonds", () => {
|
||||
act(() => root.unmount());
|
||||
});
|
||||
|
||||
const renderSegmentLane = (lastAmbiguous: boolean) => {
|
||||
const renderSegmentLane = (lastAmbiguous: boolean, clipWidthPx = 200) => {
|
||||
const host = document.createElement("div");
|
||||
document.body.append(host);
|
||||
const root = createRoot(host);
|
||||
@@ -660,7 +660,7 @@ describe("TimelineClipDiamonds", () => {
|
||||
format: "percentage",
|
||||
keyframes: [kf(0), kf(50), kf(100, { easeAmbiguous: lastAmbiguous })],
|
||||
}}
|
||||
clipWidthPx={200}
|
||||
clipWidthPx={clipWidthPx}
|
||||
clipHeightPx={48}
|
||||
accentColor="#4ba3d2"
|
||||
isSelected
|
||||
@@ -698,6 +698,21 @@ describe("TimelineClipDiamonds", () => {
|
||||
expect(segment?.style.pointerEvents).toBe("none");
|
||||
expect(ease?.style.pointerEvents).toBe("auto");
|
||||
expect(Number(segment?.style.zIndex)).toBeGreaterThan(Number(diamond?.style.zIndex));
|
||||
// Room to spare here, so the button carries the 24x24 WCAG 2.5.8 overlay.
|
||||
expect(ease?.className).toContain("before:h-6");
|
||||
act(() => root.unmount());
|
||||
});
|
||||
|
||||
it("drops the 24x24 ease overlay when the segment is too narrow to hold it", () => {
|
||||
// The segment wrapper outranks the diamonds, so an overlay wider than the
|
||||
// clear span between them would steal their clicks at fit zoom. 40px of clip
|
||||
// across three keyframes leaves well under 24px of clear span per segment.
|
||||
const { host, root } = renderSegmentLane(false, 40);
|
||||
const ease = host.querySelector<HTMLButtonElement>("[data-keyframe-ease-button]");
|
||||
|
||||
expect(ease).not.toBeNull();
|
||||
expect(ease?.className).not.toContain("before:h-6");
|
||||
expect(ease?.style.width).toBe("16px");
|
||||
act(() => root.unmount());
|
||||
});
|
||||
|
||||
|
||||
@@ -62,6 +62,12 @@ export function TimelineDiamondConnectors({
|
||||
// no source animation id (runtime-scanned) so there is no tween to target.
|
||||
const target = keyframeTarget(kf);
|
||||
const ease = kf.ease ?? globalEase;
|
||||
// connectorWidth is the clear span between the two diamonds' edges, so a
|
||||
// 24x24 target centred in it overhangs a diamond as soon as the span is
|
||||
// narrower than 24. The segment wrapper sits at z-index 3, above the
|
||||
// diamonds, so that overhang would win the hit test and steal their
|
||||
// clicks at fit zoom. Grow the target only where the room exists.
|
||||
const roomForFullTarget = connectorWidth >= 24;
|
||||
return (
|
||||
<Fragment key={`line-${i}-${previous.keyframe.percentage}-${kf.percentage}`}>
|
||||
<div
|
||||
@@ -105,8 +111,12 @@ export function TimelineDiamondConnectors({
|
||||
title={`Edit ${ease} easing`}
|
||||
// A visible 24x24 badge would collide with the diamonds either
|
||||
// side, so the WCAG 2.2 (2.5.8) target is met with a centered
|
||||
// transparent ::before overlay; the box stays 16x16.
|
||||
className="absolute flex items-center justify-center rounded opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 before:absolute before:left-1/2 before:top-1/2 before:h-6 before:w-6 before:-translate-x-1/2 before:-translate-y-1/2 before:content-['']"
|
||||
// transparent ::before overlay; the box stays 16x16. Where the
|
||||
// segment is too narrow for that overlay the button keeps its
|
||||
// 16x16 hit area, which is WCAG's target-spacing exception:
|
||||
// the neighbouring diamonds are themselves the reason it
|
||||
// cannot grow, and stealing their clicks is the worse failure.
|
||||
className={`absolute flex items-center justify-center rounded opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 ${roomForFullTarget ? "before:absolute before:left-1/2 before:top-1/2 before:h-6 before:w-6 before:-translate-x-1/2 before:-translate-y-1/2 before:content-['']" : ""}`}
|
||||
style={{
|
||||
left: "50%",
|
||||
top: "50%",
|
||||
|
||||
Reference in New Issue
Block a user