import { act } from "react";
import { createRoot } from "react-dom/client";
import { afterEach, describe, expect, it } from "vitest";
import { AnimatedPreviewBox } from ".";
const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
function stubReducedMotion(reduce: boolean): () => void {
const original = window.matchMedia;
Object.defineProperty(window, "matchMedia", {
configurable: true,
writable: true,
value: (query: string) =>
({
matches: reduce && query === REDUCED_MOTION_QUERY,
media: query,
onchange: null,
addEventListener: () => {},
removeEventListener: () => {},
addListener: () => {},
removeListener: () => {},
dispatchEvent: () => false,
}) as unknown as MediaQueryList,
});
return () => {
Object.defineProperty(window, "matchMedia", {
configurable: true,
writable: true,
value: original,
});
};
}
describe("AnimatedPreviewBox", () => {
let container: HTMLDivElement | null = null;
afterEach(() => {
container?.remove();
container = null;
});
it.each(["scale", "lift"])(
"drops the %s hover/active utilities under prefers-reduced-motion but keeps focus handling",
(animation) => {
const restore = stubReducedMotion(true);
container = document.createElement("div");
document.body.appendChild(container);
const root = createRoot(container);
act(() => {
root.render(
Preview
,
);
});
const box = container.querySelector("button > div");
expect(box?.className).not.toContain("group-hover:test-hover-class");
expect(box?.className).not.toContain("group-active:test-active-class");
// Normal focus affordance is untouched.
expect(container.querySelector("button")?.className).toContain(
"bwvs:focus-visible:ring-2",
);
act(() => root.unmount());
restore();
},
);
it("does not apply the keyframe class while hovered under prefers-reduced-motion", () => {
const restore = stubReducedMotion(true);
container = document.createElement("div");
document.body.appendChild(container);
const root = createRoot(container);
act(() => {
root.render(
Preview
,
);
});
const button = container.querySelector("button");
act(() => {
button?.dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
});
expect(
container
.querySelector("button > div")
?.classList.contains("b3-wvs-animate-swatch-bounce"),
).toBe(false);
act(() => root.unmount());
restore();
});
it("keeps scale/lift hover utilities when motion is not reduced", () => {
const restore = stubReducedMotion(false);
container = document.createElement("div");
document.body.appendChild(container);
const root = createRoot(container);
act(() => {
root.render(
Preview
,
);
});
const button = container.querySelector("button");
expect(button?.className).toContain("bwvs:h-12");
expect(button?.className).toContain("bwvs:w-12");
expect(button?.className).toContain("bwvs:flex-none");
expect(container.querySelector("button > div")?.className).toContain(
"group-hover:test-hover-class",
);
act(() => root.unmount());
restore();
});
it.each([
[false, false],
[true, true],
])("gates active classes with animateOnHover=%s", (enabled, expected) => {
container = document.createElement("div");
document.body.appendChild(container);
const root = createRoot(container);
act(() => {
root.render(
Preview
,
);
});
expect(
container
.querySelector("button > div")
?.classList.contains("test-active-class"),
).toBe(expected);
act(() => root.unmount());
});
});