From cc7d8e1aef1d8ed625455f72dba3a5a80bf0295f Mon Sep 17 00:00:00 2001 From: RealDiligent Date: Fri, 24 Jul 2026 18:50:55 +0800 Subject: [PATCH] fix(ui-kit): give PaginationLink's aria-disabled a real visual/interaction effect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PaginationLink renders an , which has no native disabled attribute, and applied no styling keyed off aria-disabled — so the five miner-ui routes that set aria-disabled at page boundaries got only a screen-reader signal, with the link staying fully opaque, hover-active, and pointer-interactive. Adds aria-disabled:pointer-events-none aria-disabled:opacity-50 at the PaginationLink level (inherited by PaginationPrevious/PaginationNext), matching the aria-disabled: styling convention sidebar.tsx and calendar.tsx already use, with a class-list regression test. Closes #8307 --- .../src/components/pagination.test.tsx | 49 +++++++++++++++++++ .../src/components/pagination.tsx | 3 ++ 2 files changed, 52 insertions(+) create mode 100644 packages/loopover-ui-kit/src/components/pagination.test.tsx diff --git a/packages/loopover-ui-kit/src/components/pagination.test.tsx b/packages/loopover-ui-kit/src/components/pagination.test.tsx new file mode 100644 index 0000000000..83c7873e3e --- /dev/null +++ b/packages/loopover-ui-kit/src/components/pagination.test.tsx @@ -0,0 +1,49 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { + PaginationLink, + PaginationNext, + PaginationPrevious, +} from "./pagination"; + +// Regression for #8307: PaginationLink (and PaginationPrevious/PaginationNext built on it) render an , +// which has no native disabled attribute — a consumer-supplied aria-disabled must produce a real +// visual/interaction cue via the aria-disabled: Tailwind variant, matching sidebar.tsx/calendar.tsx. +describe("PaginationLink aria-disabled styling (#8307)", () => { + it("carries the aria-disabled: dim + pointer-events classes when aria-disabled is set", () => { + render( + + 1 + , + ); + const link = screen.getByLabelText("prev"); + expect(link.className).toContain("aria-disabled:pointer-events-none"); + expect(link.className).toContain("aria-disabled:opacity-50"); + }); + + it("PaginationPrevious/PaginationNext inherit the aria-disabled styling from PaginationLink", () => { + render( + , + ); + for (const label of ["Go to previous page", "Go to next page"]) { + const el = screen.getByLabelText(label); + expect(el.className).toContain("aria-disabled:pointer-events-none"); + expect(el.className).toContain("aria-disabled:opacity-50"); + } + }); + + it("still renders (unchanged aria-current behavior) and the classes are present regardless — the variant only applies when aria-disabled is truthy at runtime", () => { + render( + + 2 + , + ); + const link = screen.getByLabelText("active"); + // isActive/aria-current is untouched by this fix. + expect(link.getAttribute("aria-current")).toBe("page"); + }); +}); diff --git a/packages/loopover-ui-kit/src/components/pagination.tsx b/packages/loopover-ui-kit/src/components/pagination.tsx index 1910a3006b..cae9d498c2 100644 --- a/packages/loopover-ui-kit/src/components/pagination.tsx +++ b/packages/loopover-ui-kit/src/components/pagination.tsx @@ -52,6 +52,9 @@ const PaginationLink = ({ variant: isActive ? "outline" : "ghost", size, }), + // An has no native disabled attribute, so a consumer-supplied aria-disabled needs an explicit + // visual/interaction cue -- mirrors the aria-disabled: styling sidebar.tsx/calendar.tsx already use. + "aria-disabled:pointer-events-none aria-disabled:opacity-50", className, )} {...props}