Files
manage/frontend/src/components/__tests__/HoverEditButton.test.tsx
T
Developer 688a18af22 Add mobile responsive primitives (Slice 1)
Foundation for the mobile-responsive-parity change. Adds:
- useIsMobile() hook: single source of truth for the md:768px cut (SSR-safe)
- MobileCardRow<T>: stacked card list for wide tables below md, with getRowId
  stable keys, primary field as title, optional onRowClick + actions slot
- SheetForm: full-height form host (h-[100dvh], flex column, sticky header +
  footer via flex not position:sticky) for mobile edit flows
- HoverEditButton: mobile prop (default 'always') -- always visible below md,
  hover-revealed at md+; desktop aesthetic preserved
- .mobile-touch-target CSS utility: 44x44 min hit area below md (WCAG 2.5.5)
- App.tsx refactored to use useIsMobile(); shell behavior unchanged

Tests cover primary/field rendering, onRowClick, actions slot, empty rows,
no-primary, stable keys (no duplicate-key warning), and all SheetForm
interactions. 86 tests pass; lint/build green.

MobileCardRow key strategy: uses getRowId when provided (falls back to index);
per design §trade-offs, fields are declared per-table to prioritize by mobile
importance rather than auto-derived from column defs.

Refs openspec/changes/mobile-responsive-parity/ (design §Shared primitives,
spec R1/R5/R6, tasks slice 1).
2026-06-26 12:09:21 +00:00

41 lines
1.6 KiB
TypeScript

import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { HoverEditButton } from "../HoverEditButton";
describe("HoverEditButton", () => {
it("fires onClick and exposes the default aria-label", async () => {
const onClick = vi.fn();
render(<HoverEditButton onClick={onClick} />);
const button = screen.getByRole("button", { name: "Edit" });
await userEvent.click(button);
expect(onClick).toHaveBeenCalledTimes(1);
});
it("honors a custom label", () => {
render(<HoverEditButton onClick={() => {}} label="Rename machine" />);
expect(
screen.getByRole("button", { name: "Rename machine" }),
).toBeInTheDocument();
});
it('defaults to always-visible below md (mobile="always")', () => {
render(<HoverEditButton onClick={() => {}} />);
const button = screen.getByRole("button", { name: "Edit" });
const tokens = button.className.split(/\s+/);
// The default mobile mode layers hover-reveal only at md+ via
// md:opacity-0/md:group-hover:opacity-100, so the button is visible by
// default below md (no base opacity-0 token).
expect(tokens).toContain("md:opacity-0");
expect(tokens).toContain("md:group-hover:opacity-100");
expect(tokens).not.toContain("opacity-0");
});
it('preserves the legacy opacity-0 behavior when mobile="hover"', () => {
render(<HoverEditButton onClick={() => {}} mobile="hover" />);
const button = screen.getByRole("button", { name: "Edit" });
expect(button.className).toContain("opacity-0");
expect(button.className).toContain("group-hover:opacity-100");
});
});