mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-14 07:24:15 +00:00
feat(ui): add Combobox trigger transition
- Animate combobox trigger and chevron state - Keep trigger accessible with a stable label - Cover Combobox motion with focused unit tests
This commit is contained in:
2 files changed
+97
-1
No files matched your search
@@ -0,0 +1,94 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { beforeAll, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { Combobox } from "./combobox";
|
||||
|
||||
const options = [
|
||||
{ value: "aws", label: "AWS" },
|
||||
{ value: "azure", label: "Azure" },
|
||||
{ value: "gcp", label: "GCP" },
|
||||
];
|
||||
|
||||
beforeAll(() => {
|
||||
global.ResizeObserver = class ResizeObserver {
|
||||
observe = vi.fn();
|
||||
unobserve = vi.fn();
|
||||
disconnect = vi.fn();
|
||||
} as unknown as typeof ResizeObserver;
|
||||
|
||||
Object.defineProperty(HTMLElement.prototype, "scrollIntoView", {
|
||||
configurable: true,
|
||||
value: vi.fn(),
|
||||
});
|
||||
|
||||
Object.defineProperty(HTMLElement.prototype, "hasPointerCapture", {
|
||||
configurable: true,
|
||||
value: vi.fn(() => false),
|
||||
});
|
||||
Object.defineProperty(HTMLElement.prototype, "releasePointerCapture", {
|
||||
configurable: true,
|
||||
value: vi.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
describe("Combobox", () => {
|
||||
it("renders a selectable combobox trigger", () => {
|
||||
// Given
|
||||
render(<Combobox options={options} placeholder="Select provider" />);
|
||||
|
||||
// When
|
||||
const trigger = screen.getByRole("combobox", { name: /select provider/i });
|
||||
|
||||
// Then
|
||||
expect(trigger).toBeVisible();
|
||||
expect(trigger).toHaveAttribute("aria-expanded", "false");
|
||||
});
|
||||
|
||||
it("uses visible trigger and chevron open-state motion", () => {
|
||||
// Given
|
||||
render(<Combobox options={options} placeholder="Select provider" />);
|
||||
|
||||
// When
|
||||
const trigger = screen.getByRole("combobox", { name: /select provider/i });
|
||||
const icon = trigger.querySelector("svg");
|
||||
|
||||
// Then
|
||||
expect(trigger).toHaveClass(
|
||||
"group",
|
||||
"transition-[background-color,border-color,color,box-shadow]",
|
||||
);
|
||||
expect(icon).toHaveClass(
|
||||
"transition-transform",
|
||||
"duration-200",
|
||||
"ease-out",
|
||||
"group-aria-expanded:rotate-180",
|
||||
"motion-reduce:rotate-0",
|
||||
"motion-reduce:transition-none",
|
||||
);
|
||||
});
|
||||
|
||||
it("opens with the shared Popover content motion contract", async () => {
|
||||
// Given
|
||||
const user = userEvent.setup();
|
||||
render(<Combobox options={options} placeholder="Select provider" />);
|
||||
|
||||
// When
|
||||
await user.click(
|
||||
screen.getByRole("combobox", { name: /select provider/i }),
|
||||
);
|
||||
const content = document.querySelector("[data-slot='popover-content']");
|
||||
|
||||
// Then
|
||||
expect(content).toHaveClass(
|
||||
"duration-200",
|
||||
"ease-out",
|
||||
"data-[state=open]:animate-in",
|
||||
"data-[state=open]:fade-in-0",
|
||||
"data-[state=open]:zoom-in-95",
|
||||
"motion-reduce:animate-none",
|
||||
"motion-reduce:transform-none",
|
||||
"motion-reduce:transition-none",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -113,8 +113,10 @@ export function Combobox({
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
aria-label={selectedOption ? selectedOption.label : placeholder}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
"group transition-[background-color,border-color,color,box-shadow]",
|
||||
comboboxTriggerVariants({ variant }),
|
||||
triggerClassName,
|
||||
className,
|
||||
@@ -123,7 +125,7 @@ export function Combobox({
|
||||
<span className="truncate">
|
||||
{selectedOption ? selectedOption.label : placeholder}
|
||||
</span>
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50 transition-transform duration-200 ease-out group-aria-expanded:rotate-180 motion-reduce:rotate-0 motion-reduce:transition-none" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
|
||||
Reference in new issue
Block a user