feat(ai-character): 캐릭터 목록 사용성 개선

This commit is contained in:
Yu Sung
2026-08-03 11:54:53 +09:00
parent 3759abf8b7
commit b01fda4400
5 changed files with 231 additions and 34 deletions

View File

@@ -1,4 +1,4 @@
import { fireEvent, render, screen } from "@testing-library/react";
import { fireEvent, render, screen, within } from "@testing-library/react";
import { expect, test, vi } from "vitest";
import type { PageData } from "@/shared/api/pagination";
@@ -56,3 +56,18 @@ test("ResourcePagination connects each page size label to a unique select", () =
expect(new Set(selectIds).size).toBe(selectIds.length);
expect(labels.map((label) => label.control)).toEqual(selects);
});
test("ResourcePagination separates page size from an equal-width mobile movement row", () => {
render(<ResourcePagination data={pageData} onPageChange={vi.fn()} onSizeChange={vi.fn()} />);
const sizeControls = screen.getByRole("group", { name: "페이지 크기 설정" });
const movementControls = screen.getByRole("group", { name: "페이지 이동" });
const previous = within(movementControls).getByRole("button", { name: "이전 페이지" });
const next = within(movementControls).getByRole("button", { name: "다음 페이지" });
expect(within(sizeControls).getByLabelText("페이지 크기")).toBeInTheDocument();
expect(sizeControls).not.toContainElement(previous);
expect(movementControls).toHaveClass("grid-cols-2");
expect(previous).toHaveClass("min-h-11", "w-full");
expect(next).toHaveClass("min-h-11", "w-full");
});

View File

@@ -14,21 +14,25 @@ export function ResourcePagination({ data, onPageChange, onSizeChange, sizeOptio
return (
<nav aria-label="페이지" className="flex flex-col gap-3 rounded-lg border border-border bg-card p-4 sm:flex-row sm:items-center sm:justify-between">
<p className="text-sm font-semibold text-muted-foreground"> {data.totalCount.toLocaleString("ko-KR")} · {data.page + 1}</p>
<div className="flex flex-wrap items-center gap-2">
<label className="text-sm font-semibold" htmlFor={pageSizeId}>
</label>
<select className="rounded-md border border-input bg-card px-3 py-2 text-base" id={pageSizeId} onChange={(event) => onSizeChange(Number(event.currentTarget.value))} value={data.size}>
{sizeOptions.map((size) => (
<option key={size} value={size}>{size}</option>
))}
</select>
<button className="rounded-md border border-input bg-card px-3 py-2 font-semibold hover:bg-accent disabled:opacity-60" disabled={data.page <= 0} onClick={() => onPageChange(data.page - 1)} type="button">
</button>
<button className="rounded-md border border-input bg-card px-3 py-2 font-semibold hover:bg-accent disabled:opacity-60" disabled={!data.hasNext} onClick={() => onPageChange(data.page + 1)} type="button">
</button>
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
<div aria-label="페이지 크기 설정" className="flex items-center gap-2" role="group">
<label className="text-sm font-semibold" htmlFor={pageSizeId}>
</label>
<select className="min-h-11 rounded-md border border-input bg-card px-3 py-2 text-base" id={pageSizeId} onChange={(event) => onSizeChange(Number(event.currentTarget.value))} value={data.size}>
{sizeOptions.map((size) => (
<option key={size} value={size}>{size}</option>
))}
</select>
</div>
<div aria-label="페이지 이동" className="grid grid-cols-2 gap-2 sm:flex" role="group">
<button className="min-h-11 w-full rounded-md border border-input bg-card px-3 py-2 font-semibold hover:bg-accent disabled:opacity-60 sm:w-auto" disabled={data.page <= 0} onClick={() => onPageChange(data.page - 1)} type="button">
</button>
<button className="min-h-11 w-full rounded-md border border-input bg-card px-3 py-2 font-semibold hover:bg-accent disabled:opacity-60 sm:w-auto" disabled={!data.hasNext} onClick={() => onPageChange(data.page + 1)} type="button">
</button>
</div>
</div>
</nav>
);