From 5c0d8bb8b3978cbad39b0ac7a874d4af2cd4a736 Mon Sep 17 00:00:00 2001 From: RealDiligent Date: Fri, 24 Jul 2026 18:59:51 +0800 Subject: [PATCH] fix(ui-kit): respect prefers-reduced-motion across animated ui-kit and miner-ui components 13 ui-kit components (accordion, alert-dialog, context-menu, dialog, dropdown-menu, hover-card, input-otp, menubar, navigation-menu, popover, select, sheet, tooltip) and 2 miner-ui components (streaming-text, chat/typing-indicator) animated unconditionally, so a user with the OS reduce-motion preference still got full fade/zoom/slide/bounce/blink/caret motion. Pairs each animate-* utility with motion-reduce:animate-none, the same guard skeleton.tsx/state-views.tsx/button.tsx/tabs.tsx already apply, inline at each call site (no new abstraction). Adds regression tests asserting the guard on a Radix content component (AccordionContent) and on TypingIndicator, plus a class-list assertion on StreamingText's caret. Closes #8303 --- .../components/chat/typing-indicator.test.tsx | 23 +++++++++++++ .../src/components/chat/typing-indicator.tsx | 9 +++-- .../src/components/streaming-text.tsx | 2 +- .../src/streaming-text.test.tsx | 3 ++ .../src/components/accordion.test.tsx | 33 +++++++++++++++++++ .../src/components/accordion.tsx | 2 +- .../src/components/alert-dialog.tsx | 4 +-- .../src/components/context-menu.tsx | 4 +-- .../loopover-ui-kit/src/components/dialog.tsx | 4 +-- .../src/components/dropdown-menu.tsx | 4 +-- .../src/components/hover-card.tsx | 2 +- .../src/components/input-otp.tsx | 2 +- .../src/components/menubar.tsx | 4 +-- .../src/components/navigation-menu.tsx | 6 ++-- .../src/components/popover.tsx | 2 +- .../loopover-ui-kit/src/components/select.tsx | 2 +- .../loopover-ui-kit/src/components/sheet.tsx | 4 +-- .../src/components/tooltip.tsx | 2 +- 18 files changed, 87 insertions(+), 25 deletions(-) create mode 100644 apps/loopover-miner-ui/src/components/chat/typing-indicator.test.tsx create mode 100644 packages/loopover-ui-kit/src/components/accordion.test.tsx diff --git a/apps/loopover-miner-ui/src/components/chat/typing-indicator.test.tsx b/apps/loopover-miner-ui/src/components/chat/typing-indicator.test.tsx new file mode 100644 index 0000000000..97b6024b35 --- /dev/null +++ b/apps/loopover-miner-ui/src/components/chat/typing-indicator.test.tsx @@ -0,0 +1,23 @@ +import { render } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { TypingIndicator } from "./typing-indicator"; + +// Regression for #8303: the three animate-bounce dots must each pair their animation with +// motion-reduce:animate-none so a user with the OS "reduce motion" preference set sees a static indicator +// instead of a bouncing one -- matching the guard skeleton.tsx / Spinner already provide. +describe("TypingIndicator respects prefers-reduced-motion (#8303)", () => { + it("renders three bouncing dots that each carry motion-reduce:animate-none", () => { + const { container } = render(); + const dots = container.querySelectorAll(".animate-bounce"); + expect(dots.length).toBe(3); + for (const dot of dots) { + expect(dot.className).toContain("motion-reduce:animate-none"); + } + }); + + it("renders nothing when not composing (unchanged behavior)", () => { + const { container } = render(); + expect(container.firstChild).toBeNull(); + }); +}); diff --git a/apps/loopover-miner-ui/src/components/chat/typing-indicator.tsx b/apps/loopover-miner-ui/src/components/chat/typing-indicator.tsx index 53d33e65ac..5cecec1b09 100644 --- a/apps/loopover-miner-ui/src/components/chat/typing-indicator.tsx +++ b/apps/loopover-miner-ui/src/components/chat/typing-indicator.tsx @@ -16,13 +16,16 @@ export function TypingIndicator({ {label}