memos/web/tests/calendar-cell-empty-clickable.test.tsx
boojack d722608947 fix(sidebar): stop the month calendar growing with the rail
Calendar cells were `aspect-square w-full`, so cell height was a pure
function of rail width. Dragging the rail from 224px to its 400px max
took the calendar block from 231px to 382px — a 65% jump that pushed
Views and Tags out of view, left a 12px numeral floating in a 50px box,
and turned the selected day into a 50x50 slab against a rail whose every
other row is 30px.

Split the cell in two: the button spans its whole column and takes the
pointer, while a chip inside it carries the fill, the ring and the today
dot, and stops growing at `maxSize`. A chip is therefore
`min(column, 30px)` — narrow rails are untouched (224px still yields
25px cells, 170px of grid) and wider ones spend the surplus on hit area
instead of height, holding the grid at 200px from 258px up.

The today dot stops detaching as a side effect: it is positioned against
a chip that no longer grows, rather than against a cell floor that
drifted 19px below the numeral at full width.
2026-08-06 22:59:21 +08:00

81 lines
3.6 KiB
TypeScript

import { fireEvent, render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { CalendarCell } from "@/components/ActivityCalendar/CalendarCell";
import type { CalendarDayCell } from "@/components/ActivityCalendar/types";
const makeDay = (overrides: Partial<CalendarDayCell> = {}): CalendarDayCell => ({
date: "2025-05-01",
label: "1",
count: 0,
isCurrentMonth: true,
isToday: false,
isSelected: false,
...overrides,
});
/** The button spans its column; the square chip inside it carries every visual. */
const chipOf = (button: HTMLElement) => button.firstElementChild as HTMLElement;
describe("CalendarCell empty-day clickability", () => {
it("fires onClick for an in-month day with count=0", () => {
const onClick = vi.fn();
render(<CalendarCell day={makeDay()} maxCount={5} tooltipText="May 1, 2025" onClick={onClick} />);
const button = screen.getByRole("button", { name: /May 1, 2025/ });
fireEvent.click(button);
expect(onClick).toHaveBeenCalledWith("2025-05-01");
});
it("renders an empty in-month day as interactive (tabIndex 0, not aria-disabled)", () => {
render(<CalendarCell day={makeDay()} maxCount={5} tooltipText="May 1, 2025" onClick={() => {}} />);
const button = screen.getByRole("button", { name: /May 1, 2025/ });
expect(button).toHaveAttribute("tabindex", "0");
expect(button).toHaveAttribute("aria-disabled", "false");
expect(button).not.toHaveAttribute("data-slot", "tooltip-trigger");
expect(chipOf(button)).toHaveClass("bg-transparent");
});
it("still renders a populated in-month day as interactive", () => {
const onClick = vi.fn();
render(<CalendarCell day={makeDay({ count: 3 })} maxCount={5} tooltipText="May 1, 2025" onClick={onClick} />);
const button = screen.getByRole("button", { name: /May 1, 2025/ });
expect(button).toHaveAttribute("data-slot", "tooltip-trigger");
fireEvent.click(button);
expect(onClick).toHaveBeenCalledWith("2025-05-01");
});
it("marks today with a dot without changing the numeral weight", () => {
render(<CalendarCell day={makeDay({ isToday: true })} maxCount={5} tooltipText="May 1, 2025" onClick={() => {}} />);
const button = screen.getByRole("button", { name: /May 1, 2025/ });
expect(button).not.toHaveClass("font-semibold", "font-bold");
expect(button.querySelector('[aria-hidden="true"]')).toHaveClass("rounded-full");
});
it("uses an inset ring for selection without changing the numeral weight", () => {
render(<CalendarCell day={makeDay({ isSelected: true })} maxCount={5} tooltipText="May 1, 2025" onClick={() => {}} />);
const button = screen.getByRole("button", { name: /selected/ });
expect(chipOf(button)).toHaveClass("ring-2", "ring-inset");
expect(chipOf(button)).not.toHaveClass("font-semibold", "font-bold");
});
it("caps the chip so a wider container buys hit area, not calendar height", () => {
render(<CalendarCell day={makeDay()} maxCount={5} tooltipText="May 1, 2025" onClick={() => {}} />);
const button = screen.getByRole("button", { name: /May 1, 2025/ });
// The square lives on the capped chip. Putting it back on the button would make row
// height track the column width again, which is what made a widened rail so tall.
expect(button).not.toHaveClass("aspect-square");
expect(chipOf(button)).toHaveClass("aspect-square", "max-w-[30px]");
});
it("does not render out-of-month days as interactive (no role=button)", () => {
render(<CalendarCell day={makeDay({ isCurrentMonth: false })} maxCount={5} tooltipText="May 1, 2025" onClick={() => {}} />);
expect(screen.queryByRole("button")).toBeNull();
});
});