Skip to content

refactor: Migrate SeriesTable to the shared table state hook - #404

Merged
frankieramirez merged 2 commits into
mainfrom
refactor/migrate-series-table-state
Jul 28, 2026
Merged

refactor: Migrate SeriesTable to the shared table state hook#404
frankieramirez merged 2 commits into
mainfrom
refactor/migrate-series-table-state

Conversation

@frankieramirez

@frankieramirez frankieramirez commented Jul 28, 2026

Copy link
Copy Markdown
Owner

PR 2 of the four-PR migration cut by wayfinder #366. Closes #394; part of the #353 map.

SeriesTable migrates to useTableState in place — it renders its own bulk bar, so per #359 it calls the hook itself; SeriesListPage and the component's props are untouched.

What changed

Parity — the invariant, old and new side by side

SeriesTable.test.tsx renders the same <SeriesTable data={…} /> interface, so its four table-state tests and #390's selection tests pass verbatim — the file is untouched except the one assertion #381 pinned expressly so this PR would surface the change rather than hide it.

Invariant: an out-of-range deep link never renders an empty table. That half is unchanged — ?page=99 over 63 rows still renders the last real page. The URL half inverts, because the guarded clamp effect that rewrote the URL is deleted:

  // 63 rows at 20 a page is pages 0–3, so the last page is Series 61–63.
  expect(screen.getByText("Series 61")).toBeTruthy();
- expect(new URLSearchParams(window.location.search).get("page")).toBe("3");
+ expect(new URLSearchParams(window.location.search).get("page")).toBe("99");

The rewrite could not tell "rows have not arrived" from "genuinely out of range" — that indistinguishability was the deep-link bug (#372, #381) — so the URL now stays exactly as the user supplied it.

Changeset

patch — the migration's only one (#366, per #386's operator-observable rule): the out-of-range URL rewrite is operator-visible, and #381 shipped the equivalent change as patch.

Suite: 29 files / 173 tests green; lint:modern, lint:backend, lint:generated, lint:frontend and tsc all pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PCo8dyZq6YvkQmvK4zNjyg

Summary by CodeRabbit

  • Bug Fixes

    • Preserved out-of-range page numbers in library URLs while displaying the last available page.
    • Improved table selection and bulk-action behavior, including safer confirmation handling.
    • Maintained search, sorting, filtering, and deep-link state more consistently across table interactions.
  • Tests

    • Added coverage confirming that out-of-range page parameters remain unchanged in the URL.

PR 2 of the #366 cut (#394). SeriesTable now calls useTableState in place
(it renders its own bulk bar, per #359) with the URL store injected from
tableUrlStore.ts (#377), selectAllScope: "page" (#382), and pagination
handed to TanStack's autoResetPageIndex (#360).

- The page-clamp effect is deleted; the clamp is render-time and
  display-only, composed over the URL store in the caller's adapter. An
  out-of-range ?page=99 renders the last page and keeps its URL — the
  one assertion #381 pinned expressly so this change would surface here,
  inverted in this PR and nowhere else.
- search folds into the shared params map under the same key, replacing
  the deprecated throttleMs: 300 with limitUrlUpdates: throttle(300).
- Six hand-rolled setRowSelection({}) calls become clearSelection();
  the hook owns selection pruning and the deep-link auto-reset arming.
- confirmDelete derives from the selection state object it was armed
  against, replacing the reset that lived in onRowSelectionChange.
- SeriesTable's line leaves the ESLint ratchet allowlist.

Suite: 29 files / 173 tests green; SeriesTable.test.tsx untouched except
the pinned inversion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCo8dyZq6YvkQmvK4zNjyg
@changeset-bot

changeset-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b406789

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
comicarr Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@frankieramirez, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 53 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 04423c07-0456-4a2f-81b9-ce7cad8ea3fd

📥 Commits

Reviewing files that changed from the base of the PR and between 438590a and b406789.

📒 Files selected for processing (2)
  • frontend/src/components/series/SeriesTable.tsx
  • frontend/tests/components/SeriesTable.test.tsx
📝 Walkthrough

Walkthrough

Changes

SeriesTable shared state migration

Layer / File(s) Summary
Shared table state setup
frontend/src/components/series/SeriesTable.tsx
Moves table, URL, pagination, sorting, and page-scoped selection state to shared hooks while keeping render-time page clamping display-only.
Selection and table controls
frontend/src/components/series/SeriesTable.tsx
Updates bulk actions, confirmation, view toggles, filters, search, sorting, and select-all to use shared table helpers.
Behavior validation and release metadata
frontend/tests/components/SeriesTable.test.tsx, frontend/eslint.config.js, .changeset/library-out-of-range-page-url-kept.md
Verifies that an out-of-range page URL remains unchanged and records the lint and patch-release updates.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

  • frankieramirez/comicarr#381 — Defines the deep-link behavior where the displayed page clamps while the original URL parameter is preserved.
  • frankieramirez/comicarr#353 — Tracks the shared useTableState migration implemented in SeriesTable.
  • frankieramirez/comicarr#322 — Covers centralized table selection, pagination, sorting, and URL synchronization.

Possibly related PRs

Poem

A bunny hops through pages bright,
While URLs keep their chosen flight.
Shared hooks guide each sort and row,
Confirmed deletes now clearly show.
The last page waits, the link stays true—
A patch-sized cheer from me to you! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: migrating SeriesTable to the shared table state hook.
Linked Issues check ✅ Passed The changes match #394’s migration goals: shared table state, URL store integration, selection handling, clamp removal, tests, changeset, and ESLint cleanup.
Out of Scope Changes check ✅ Passed No unrelated changes stand out; the PR stays focused on the SeriesTable migration, its tests, changeset, and lint allowlist update.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/migrate-series-table-state

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/components/series/SeriesTable.tsx`:
- Around line 130-137: Update the pagination clamp in SeriesTable’s
pageIndex/maxPage logic to calculate the maximum display page from the globally
search-filtered rows, using the same filtering behavior as useTableState. Keep
the raw URL pageIndex unchanged in urlStore, and preserve the existing state
override so deep links with a search term cannot render an empty page.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e9119f2a-1327-4cfe-876e-c6b3d5bc5b2b

📥 Commits

Reviewing files that changed from the base of the PR and between 78076b1 and 438590a.

📒 Files selected for processing (4)
  • .changeset/library-out-of-range-page-url-kept.md
  • frontend/eslint.config.js
  • frontend/src/components/series/SeriesTable.tsx
  • frontend/tests/components/SeriesTable.test.tsx
💤 Files with no reviewable changes (1)
  • frontend/eslint.config.js

Comment thread frontend/src/components/series/SeriesTable.tsx Outdated
CodeRabbit finding on #404: the display clamp counted only the
domain-filtered rows, but the global search runs inside useTableState —
so ?page=99&search=<single match> clamped to the last unsearched page
and rendered an empty table. The clamp now reproduces TanStack's
includesString over the same four accessor columns, and a new test pins
the searched out-of-range deep link (verified red against the
domain-only clamp).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCo8dyZq6YvkQmvK4zNjyg
@frankieramirez
frankieramirez merged commit d1b93d0 into main Jul 28, 2026
16 checks passed
@frankieramirez
frankieramirez deleted the refactor/migrate-series-table-state branch July 28, 2026 16:50
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.

Migrate SeriesTable to useTableState

1 participant