Files
reactbin/specs/018-pagination-controls/plan.md
agatha 0ad82e60ac Feat: Replace pagination bar with numbered page buttons and chevron controls
Adds « ‹ [1][2][3][4] › » navigation to the library. Page window
slides to keep the current page in view. Prev/next/first/last controls
are always rendered but disabled at their respective bounds. Also wires
up karmaConfig in angular.json so FirefoxHeadless is used for tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 18:11:18 +00:00

4.1 KiB
Raw Blame History

Implementation Plan: Pagination Controls Redesign

Branch: 018-pagination-controls | Date: 2026-05-10 | Spec: spec.md

Summary

Replace the existing "← Previous / Page X of Y / Next →" pagination bar in LibraryComponent with six controls: first-page («), previous-page (), up to four numbered page buttons, next-page (), and last-page (»). All logic stays in the existing component — no new component is introduced (§2.6: no speculative abstraction, only one paginated view exists).

Technical Context

Language/Version: TypeScript (strict mode) Primary Dependencies: Angular (latest stable), Karma + Jasmine Storage: N/A — no data layer changes Testing: Angular TestBed unit tests (component spec) Target Platform: Browser SPA Project Type: Web application — UI only Performance Goals: No measurable regression in render or navigation time Constraints: ESLint + Prettier must pass (§7.3); all existing tests must continue to pass (§5.4) Scale/Scope: Single component change; one paginated view in the app

Constitution Check

Principle Status Notes
§2.6 No speculative abstraction PASS Pagination logic stays inline in LibraryComponent; no new component introduced
§5.1 Tests alongside implementation PASS Spec tests for window algorithm, disabled states, and navigation covered in tasks
§5.2 Test pyramid PASS Unit tests via TestBed; no integration or E2E tests required for a template change
§5.4 Suite must pass before done PASS Gate enforced per task
§7.3 Lint/format enforced PASS ESLint + Prettier gate on all tasks
§8 Scope boundaries PASS No out-of-scope work touched

No violations. No Complexity Tracking table needed.

Project Structure

Documentation (this feature)

specs/018-pagination-controls/
├── plan.md         ← this file
├── research.md
└── tasks.md        (generated by /speckit-tasks)

Source Code (changed files only)

ui/src/app/library/
├── library.component.ts       ← template, styles, class (page window getter + goToPage/firstPage/lastPage methods)
└── library.component.spec.ts  ← new tests for window algorithm, disabled states, button navigation

No new files. No API changes. No data model changes.

Page Window Algorithm

Given currentPage (1-based) and totalPages, compute the array of up to four page numbers to display:

start = max(1, currentPage - 1)
end   = min(totalPages, start + 3)
start = max(1, end - 3)          ← re-anchor if near the end
pages = [start .. end]

Examples:

  • Page 1 of 20 → [1, 2, 3, 4]
  • Page 7 of 20 → [6, 7, 8, 9]
  • Page 19 of 20 → [17, 18, 19, 20]
  • Page 2 of 3 → [1, 2, 3]

New Controls Layout

«    [1] [2] [3] [4]    »
  • « disabled when currentPage === 1
  • disabled when currentPage === 1
  • Active page button has distinct active style
  • disabled when currentPage === totalPages
  • » disabled when currentPage === totalPages
  • Entire bar hidden when totalPages <= 1 (existing behaviour retained)

Methods to Add/Change

Method Change
get pageWindow(): number[] New getter — returns array of up to 4 page numbers
goToPage(page: number) New — navigates to arbitrary page number
firstPage() New — navigates to page 1
lastPage() New — navigates to last page
nextPage() Existing — no change needed
prevPage() Existing — no change needed

Research

No unknowns. Tech stack is fixed (Angular/TypeScript). The windowing algorithm is a standard sliding-window with boundary clamping. No external research required.

Decision: Keep all logic in LibraryComponent (no child component). Rationale: §2.6 prohibits speculative abstraction; only one paginated view exists in the app. Extracting a PaginationComponent would be justified only when a second use case appears. Alternatives considered: Standalone PaginationComponent — rejected; no second consumer.