Skip to content

fix(dashboard): use client-side navigation in pagination to prevent full page reloads (#2305) - #2307

Open
Dewin wants to merge 1 commit into
CapSoftware:mainfrom
Dewin:fix/dashboard-pagination-client-navigation-2305
Open

Dewin wants to merge 1 commit into
CapSoftware:mainfrom
Dewin:fix/dashboard-pagination-client-navigation-2305

Conversation

@Dewin

@Dewin Dewin commented Sep 17, 2026

Copy link
Copy Markdown

What does this PR do?

Resolves issue #2305 (Linear: CAP-813) where pagination controls in the My Caps dashboard (/dashboard/caps) rendered plain <a> tags, causing a full browser/app shell reload on every page transition.

Root Cause

  1. @cap/ui's PaginationLink rendered an un-customizable HTML <a> tag with no slot delegation.
  2. In CapPagination.tsx, page numbers and Next/Previous navigation targets were passed directly to PaginationLink / PaginationPrevious / PaginationNext without client-side routing, triggering full page reloads and re-mounting the dashboard shell on every page click.

Changes

  1. packages/ui/src/components/Pagination.tsx:
    • Added asChild?: boolean prop to PaginationLinkProps utilizing @radix-ui/react-slot (Slot), mirroring the established pattern in Button.tsx.
    • Updated PaginationPrevious and PaginationNext to support asChild, automatically preserving and injecting standard chevron icons and labels into client-side links (next/link).
  2. apps/web/app/(org)/dashboard/caps/components/CapPagination.tsx:
    • Wrapped pagination targets (PaginationPrevious, PaginationLink, PaginationNext) in Next.js <Link href={...} scroll={false}> with asChild.
    • Ensures transitions are handled by Next.js client-side router without re-mounting the app shell or jumping the page scroll.
  3. apps/web/__tests__/unit/cap-pagination.test.ts:
    • Added comprehensive unit tests asserting slot delegation, Next.js link wrapping, scroll={false} setting, and dynamic hrefForPage propagation.

Verification

  • Verified Radix Slot integration matches @cap/ui conventions.
  • Clean git diff with zero trailing whitespace.

RetriggerConfidence Score: 4/5

The navigation implementation appears safe, but the repository’s explicit comments requirement must be satisfied before merging; the test-execution and rendering gaps are additional non-blocking quality concerns.

Findings

  1. P2 Test Missing From CI
  2. P2 Links Are Never Rendered
  3. P2 Comments Narrate Assertions
Fix with agent prompt
### Issue 1
apps/web/__tests__/unit/cap-pagination.test.ts:16
The new pagination test is absent from the explicit web-test lists executed by CI, so these assertions can regress without a required check failing. Add this file to the relevant CI test invocation or run the complete web unit-test suite.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

### Issue 2
apps/web/__tests__/unit/cap-pagination.test.ts:40-68
Calling `CapPagination` directly only inspects unevaluated React element props; it never executes `PaginationPrevious`, `PaginationNext`, or Radix `Slot`. The test can therefore remain green if the final anchors lose their href, labels, icons, or delegated props. Render the component in a DOM environment and assert the resulting links.

### Issue 3
apps/web/__tests__/unit/cap-pagination.test.ts:50
The comments here and at lines 59 and 78 merely narrate the immediately following assertions. The repository requires defaulting to no code comments and explicitly prohibits comments that only describe what the code does. Remove these comments or replace them with genuinely non-obvious context; this repository requirement must be satisfied before merging.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

This PR changes dashboard pagination to delegate shared pagination styling and accessibility props through Radix Slot onto Next.js links, allowing page transitions without reloading the persistent dashboard shell.

  • Adds asChild support to shared pagination links and preserves default previous/next labels and chevrons.
  • Uses Next.js Link with scroll disabled for dashboard page-number and directional controls.
  • Adds unit coverage, although the new suite is not currently executed by CI and does not render the final anchors.

Reviews (1) · Last reviewed commit: "fix(dashboard): use client-side navigati..."

} from "@cap/ui";

describe("CapPagination and Client Navigation (Issue #2305)", () => {
it("verifies CapPagination imports Link from next/link and passes asChild", () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Test Missing From CI

The new pagination test is absent from the explicit web-test lists executed by CI, so these assertions can regress without a required check failing. Add this file to the relevant CI test invocation or run the complete web unit-test suite.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/__tests__/unit/cap-pagination.test.ts
Line: 16

Comment:
**Test Missing From CI**

The new pagination test is absent from the explicit web-test lists executed by CI, so these assertions can regress without a required check failing. Add this file to the relevant CI test invocation or run the complete web unit-test suite.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +40 to +68
it("renders client Link elements inside PaginationPrevious, PaginationLink, and PaginationNext", () => {
const element = CapPagination({
currentPage: 2,
totalPages: 5,
}) as React.ReactElement<{ children: React.ReactElement<{ children: React.ReactNode[] }> }>;

expect(element).toBeDefined();
expect(element.type).toBe(Pagination);

const content = element.props.children;
expect(content.type).toBe(PaginationContent);

const items = React.Children.toArray(content.props.children) as React.ReactElement[];
expect(items.length).toBeGreaterThan(0);

// First item should be PaginationPrevious with asChild
const prevItem = items[0].props.children as React.ReactElement<{ asChild?: boolean; children: React.ReactElement<{ href: string; scroll: boolean }> }>;
expect(prevItem.type).toBe(PaginationPrevious);
expect(prevItem.props.asChild).toBe(true);
expect(prevItem.props.children.props.href).toBe("/dashboard/caps?page=1");
expect(prevItem.props.children.props.scroll).toBe(false);

// Last item should be PaginationNext with asChild
const nextItem = items[items.length - 1].props.children as React.ReactElement<{ asChild?: boolean; children: React.ReactElement<{ href: string; scroll: boolean }> }>;
expect(nextItem.type).toBe(PaginationNext);
expect(nextItem.props.asChild).toBe(true);
expect(nextItem.props.children.props.href).toBe("/dashboard/caps?page=3");
expect(nextItem.props.children.props.scroll).toBe(false);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Links Are Never Rendered

Calling CapPagination directly only inspects unevaluated React element props; it never executes PaginationPrevious, PaginationNext, or Radix Slot. The test can therefore remain green if the final anchors lose their href, labels, icons, or delegated props. Render the component in a DOM environment and assert the resulting links.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/__tests__/unit/cap-pagination.test.ts
Line: 40-68

Comment:
**Links Are Never Rendered**

Calling `CapPagination` directly only inspects unevaluated React element props; it never executes `PaginationPrevious`, `PaginationNext`, or Radix `Slot`. The test can therefore remain green if the final anchors lose their href, labels, icons, or delegated props. Render the component in a DOM environment and assert the resulting links.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

expect(element.type).toBe(Pagination);

const content = element.props.children;
expect(content.type).toBe(PaginationContent);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Comments Narrate Assertions

The comments here and at lines 59 and 78 merely narrate the immediately following assertions. The repository requires defaulting to no code comments and explicitly prohibits comments that only describe what the code does. Remove these comments or replace them with genuinely non-obvious context; this repository requirement must be satisfied before merging.

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/__tests__/unit/cap-pagination.test.ts
Line: 50

Comment:
**Comments Narrate Assertions**

The comments here and at lines 59 and 78 merely narrate the immediately following assertions. The repository requires defaulting to no code comments and explicitly prohibits comments that only describe what the code does. Remove these comments or replace them with genuinely non-obvious context; this repository requirement must be satisfied before merging.

**Context Used:** AGENTS.md ([source](https://github.com/capsoftware/cap/blob/main/AGENTS.md))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dashboard pagination causes full page reloads on self-hosted Cap

1 participant