mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-05 03:12:14 +00:00
fix(ui): animate collapsible height and degrade tree motion under reduced motion
This commit is contained in:
@@ -24,16 +24,8 @@ describe("Collapsible", () => {
|
||||
expect(content).toHaveAttribute("data-slot", "collapsible-content");
|
||||
expect(content).toHaveClass(
|
||||
"overflow-hidden",
|
||||
"duration-200",
|
||||
"ease-out",
|
||||
"data-[state=open]:animate-in",
|
||||
"data-[state=open]:fade-in-0",
|
||||
"data-[state=open]:slide-in-from-top-1",
|
||||
"data-[state=closed]:animate-out",
|
||||
"data-[state=closed]:fade-out-0",
|
||||
"data-[state=closed]:slide-out-to-top-1",
|
||||
"data-[state=closed]:duration-150",
|
||||
"data-[state=closed]:ease-in",
|
||||
"data-[state=open]:animate-collapsible-down",
|
||||
"data-[state=closed]:animate-collapsible-up",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -52,7 +44,6 @@ describe("Collapsible", () => {
|
||||
// Then
|
||||
expect(content).toHaveClass(
|
||||
"motion-reduce:animate-none",
|
||||
"motion-reduce:transform-none",
|
||||
"motion-reduce:transition-none",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -29,11 +29,10 @@ function CollapsibleContent({
|
||||
<CollapsiblePrimitive.CollapsibleContent
|
||||
data-slot="collapsible-content"
|
||||
className={cn(
|
||||
"overflow-hidden duration-200 ease-out",
|
||||
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-top-1",
|
||||
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-1",
|
||||
"data-[state=closed]:duration-150 data-[state=closed]:ease-in",
|
||||
"motion-reduce:transform-none motion-reduce:animate-none motion-reduce:transition-none",
|
||||
"overflow-hidden",
|
||||
"data-[state=open]:animate-collapsible-down",
|
||||
"data-[state=closed]:animate-collapsible-up",
|
||||
"motion-reduce:animate-none motion-reduce:transition-none",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
|
||||
import { ChevronRightIcon } from "lucide-react";
|
||||
import { KeyboardEvent } from "react";
|
||||
|
||||
@@ -14,6 +14,24 @@ import { TreeSpinner } from "./tree-spinner";
|
||||
import { TreeStatusIndicator } from "./tree-status-indicator";
|
||||
import { getAllDescendantIds, getTreeNodePadding } from "./utils";
|
||||
|
||||
export function getTreeChildrenMotion(shouldReduceMotion: boolean) {
|
||||
if (shouldReduceMotion) {
|
||||
return {
|
||||
initial: { opacity: 0 },
|
||||
animate: { opacity: 1 },
|
||||
exit: { opacity: 0 },
|
||||
transition: { duration: 0, ease: "easeInOut" as const },
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
initial: { opacity: 0, height: 0 },
|
||||
animate: { opacity: 1, height: "auto" as const },
|
||||
exit: { opacity: 0, height: 0 },
|
||||
transition: { duration: 0.2, ease: "easeInOut" as const },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* TreeNode component for rendering expandable nodes with children.
|
||||
*
|
||||
@@ -36,6 +54,8 @@ export function TreeNode({
|
||||
renderItem,
|
||||
enableSelectChildren,
|
||||
}: TreeNodeProps) {
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
const childrenMotion = getTreeChildrenMotion(!!shouldReduceMotion);
|
||||
const isExpanded = expandedIds.includes(item.id);
|
||||
const isSelected = selectedIds.includes(item.id);
|
||||
const statusIcon =
|
||||
@@ -166,10 +186,10 @@ export function TreeNode({
|
||||
{isExpanded && (
|
||||
<motion.ul
|
||||
key={`children-${item.id}`}
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: "auto" }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
transition={{ duration: 0.2, ease: "easeInOut" }}
|
||||
initial={childrenMotion.initial}
|
||||
animate={childrenMotion.animate}
|
||||
exit={childrenMotion.exit}
|
||||
transition={childrenMotion.transition}
|
||||
className="mt-1 space-y-1 overflow-hidden"
|
||||
role="group"
|
||||
>
|
||||
|
||||
@@ -1,8 +1,32 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { getTreeChildrenMotion } from "./tree-node";
|
||||
import { TreeView } from "./tree-view";
|
||||
|
||||
describe("getTreeChildrenMotion", () => {
|
||||
it("animates height when motion is allowed", () => {
|
||||
// Given / When
|
||||
const motion = getTreeChildrenMotion(false);
|
||||
|
||||
// Then
|
||||
expect(motion.initial).toHaveProperty("height", 0);
|
||||
expect(motion.animate).toHaveProperty("height", "auto");
|
||||
expect(motion.transition.duration).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("degrades to opacity-only with no height under reduced motion", () => {
|
||||
// Given / When
|
||||
const motion = getTreeChildrenMotion(true);
|
||||
|
||||
// Then
|
||||
expect(motion.initial).not.toHaveProperty("height");
|
||||
expect(motion.animate).not.toHaveProperty("height");
|
||||
expect(motion.exit).not.toHaveProperty("height");
|
||||
expect(motion.transition.duration).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
const treeData = [
|
||||
{
|
||||
id: "org-1",
|
||||
|
||||
Reference in New Issue
Block a user