diff --git a/src/features/online-board/components/DayTabs/DayTabButton.test.tsx b/src/features/online-board/components/DayTabs/DayTabButton.test.tsx new file mode 100644 index 00000000..a4f2cace --- /dev/null +++ b/src/features/online-board/components/DayTabs/DayTabButton.test.tsx @@ -0,0 +1,50 @@ +// @vitest-environment jsdom +import { describe, it, expect, vi } from "vitest"; +import { render, screen, fireEvent } from "@testing-library/react"; +import { DayTabButton } from "./DayTabButton.js"; + +describe("DayTabButton", () => { + it("has data-testid based on date", () => { + render( {}} />); + expect(screen.getByTestId("day-tab-20260416")).toBeTruthy(); + }); + + it("renders weekday, day number, and month", () => { + render( {}} />); + expect(screen.getByText("16")).toBeTruthy(); + expect(screen.getByText(/Apr/i)).toBeTruthy(); + expect(screen.getByText(/Thu/i)).toBeTruthy(); + }); + + it("calls onClick with date when enabled", () => { + const onClick = vi.fn(); + render(); + fireEvent.click(screen.getByTestId("day-tab-20260416")); + expect(onClick).toHaveBeenCalledWith("20260416"); + }); + + it("does not call onClick when disabled", () => { + const onClick = vi.fn(); + render(); + fireEvent.click(screen.getByTestId("day-tab-20260416")); + expect(onClick).not.toHaveBeenCalled(); + }); + + it("applies --active modifier when active", () => { + render( {}} />); + const el = screen.getByTestId("day-tab-20260416"); + expect(el.className).toMatch(/--active/); + }); + + it("applies --disabled modifier when disabled", () => { + render( {}} />); + const el = screen.getByTestId("day-tab-20260416"); + expect(el.className).toMatch(/--disabled/); + }); + + it("is disabled attribute set when disabled", () => { + render( {}} />); + const btn = screen.getByTestId("day-tab-20260416") as HTMLButtonElement; + expect(btn.disabled).toBe(true); + }); +}); diff --git a/src/features/online-board/components/DayTabs/DayTabButton.tsx b/src/features/online-board/components/DayTabs/DayTabButton.tsx new file mode 100644 index 00000000..f219b775 --- /dev/null +++ b/src/features/online-board/components/DayTabs/DayTabButton.tsx @@ -0,0 +1,48 @@ +import type { FC } from "react"; +import { parseYyyymmdd } from "./dateRange.js"; +import "./DayTabs.scss"; + +export interface DayTabButtonProps { + date: string; + isActive: boolean; + isDisabled: boolean; + locale: string; + onClick: (date: string) => void; +} + +export const DayTabButton: FC = ({ + date, + isActive, + isDisabled, + locale, + onClick, +}) => { + const d = parseYyyymmdd(date); + const weekday = new Intl.DateTimeFormat(locale, { weekday: "short" }).format(d); + const day = new Intl.DateTimeFormat(locale, { day: "numeric" }).format(d); + const month = new Intl.DateTimeFormat(locale, { month: "short" }).format(d); + + const classes = [ + "day-tab", + isActive ? "day-tab--active" : "", + isDisabled ? "day-tab--disabled" : "", + ] + .filter(Boolean) + .join(" "); + + return ( + + ); +}; diff --git a/src/features/online-board/components/DayTabs/DayTabs.scss b/src/features/online-board/components/DayTabs/DayTabs.scss new file mode 100644 index 00000000..106949e0 --- /dev/null +++ b/src/features/online-board/components/DayTabs/DayTabs.scss @@ -0,0 +1,40 @@ +.day-tab { + padding: 12px 8px; + text-align: center; + cursor: pointer; + background: #e8f0f7; + color: #2060c0; + border: none; + border-right: 1px solid #d0dae5; + + &:last-child { + border-right: none; + } + + &--active { + background: #fff; + color: #1a3a5c; + font-weight: 600; + } + + &--disabled { + opacity: 0.5; + cursor: not-allowed; + } + + &__weekday { + font-size: 12px; + display: block; + } + + &__day { + font-size: 20px; + font-weight: 500; + display: block; + } + + &__month { + font-size: 12px; + display: block; + } +}