feat(design): the three 375 rows (E28 S3f3)
- Download row: answers to its own width (container query); below 40rem
it stacks: picture beside a two-line title, the meta line across the
row (one line, never wraps: zoomed on the iPad its pieces broke apart),
then the source link and the actions on one wrapping line. The paste
field takes a line of its own when the buttons would squeeze it.
- YT playlist row: below 42rem the length rides on the picture and the
title runs two lines over the channel.
- Channels tag row: the suggestions take a line of their own below
40rem (the chip lane was 0 px and the row's menu off the screen).
- Pager: on a phone the arrows drop their words (names kept), and the
size label stays one line.
- Lint: a class split where the interpolation is the variant itself
(${NARROW}:w-24) is refused too; it had shipped with no CSS.
This commit is contained in:
@@ -110,3 +110,28 @@ describe("Pager · the page box follows the page without an effect", () => {
|
||||
expect(box).toHaveProperty("value", "3");
|
||||
});
|
||||
});
|
||||
|
||||
// E28 S3f3: on a phone the row ran 18px past a 375 screen. The arrows keep their names (a screen
|
||||
// reader still hears Previous / Next) and only the visible words step aside below `sm`; the size label
|
||||
// stays one line rather than a word per line.
|
||||
describe("Pager · on a phone", () => {
|
||||
it("hides only the visible words of Previous / Next, never their names", () => {
|
||||
render(
|
||||
<Pager
|
||||
page={1}
|
||||
size={10}
|
||||
total={120}
|
||||
pageSizeOptions={[10, 25]}
|
||||
onPageChange={() => {}}
|
||||
onSizeChange={() => {}}
|
||||
sizeLabel="Rows per page"
|
||||
/>,
|
||||
);
|
||||
for (const name of ["datatable.pager.prev", "datatable.pager.next"]) {
|
||||
const btn = screen.getByRole("button", { name });
|
||||
const word = [...btn.querySelectorAll("span")].find((s) => s.textContent === name)!;
|
||||
expect(word.className.split(/\s+/)).toContain("max-sm:sr-only");
|
||||
}
|
||||
expect(screen.getByText("Rows per page").className).toContain("whitespace-nowrap");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -74,7 +74,9 @@ export default function Pager({
|
||||
onClick={() => goToPage(Math.max(0, safePage - 1))}
|
||||
disabled={safePage === 0}
|
||||
>
|
||||
{t("datatable.pager.prev")}
|
||||
{/* A phone keeps the arrows and the page box: the two words pushed the row 18px past a
|
||||
375 screen (E28 S3f3). The name stays for a screen reader. */}
|
||||
<span className="max-sm:sr-only">{t("datatable.pager.prev")}</span>
|
||||
</Button>
|
||||
<span className="text-muted flex items-center gap-1.5">
|
||||
{t("datatable.pager.pageLabel")}
|
||||
@@ -98,14 +100,17 @@ export default function Pager({
|
||||
onClick={() => goToPage(Math.min(totalPages - 1, safePage + 1))}
|
||||
disabled={safePage >= totalPages - 1}
|
||||
>
|
||||
{t("datatable.pager.next")}
|
||||
<span className="max-sm:sr-only">{t("datatable.pager.next")}</span>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{total > 0 && (
|
||||
<div className="flex items-center gap-2 text-muted">
|
||||
<span aria-hidden="true">{sizeLabel}</span>
|
||||
// The label never breaks into a word per line: short of room it goes above the sizes.
|
||||
<div className="flex flex-wrap items-center gap-2 text-muted">
|
||||
<span aria-hidden="true" className="whitespace-nowrap">
|
||||
{sizeLabel}
|
||||
</span>
|
||||
{/* A handful of fixed sizes plus "All": segmented, the house control for a short fixed set. */}
|
||||
<SegmentedToggle
|
||||
ariaLabel={sizeLabel}
|
||||
|
||||
Reference in New Issue
Block a user