Skip to content

fix(admin): auto-fetch API pages when paginating content list#273

Open
all3f0r1 wants to merge 2 commits intoemdash-cms:mainfrom
all3f0r1:fix/admin-content-list-pagination
Open

fix(admin): auto-fetch API pages when paginating content list#273
all3f0r1 wants to merge 2 commits intoemdash-cms:mainfrom
all3f0r1:fix/admin-content-list-pagination

Conversation

@all3f0r1
Copy link
Copy Markdown
Contributor

@all3f0r1 all3f0r1 commented Apr 5, 2026

What does this PR do?

The admin content list only displayed the first batch of items from the API (up to 100). Client-side pagination (pages of 20) cycled through those items but never fetched additional pages via nextCursor, making items beyond the first batch completely invisible in the admin.

This replaces the manual "Load More" button with a useEffect that automatically triggers fetchNextPage() when the user reaches the last client-side page and more API data is available. The item count also now shows "100+" when more pages exist.

Closes #266

Type of change

  • Bug fix
  • Feature (requires approved Discussion)
  • Refactor (no behavior change)
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm --silent lint:json | jq '.diagnostics | length' returns 0
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/...

AI-generated code disclosure

  • This PR includes AI-generated code

Screenshots / test output

N/A — behavioral change only (no visual changes).

The content list only displayed the first batch of items from the API
(up to 100). Client-side pagination cycled through those items but never
fetched additional pages via nextCursor, making items beyond the first
batch invisible.

Replace the manual "Load More" button with an effect that automatically
triggers fetchNextPage() when the user reaches the last client-side
page and more API data is available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 5, 2026

🦋 Changeset detected

Latest commit: 6c38d93

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

This PR includes changesets to release 10 packages
Name Type
@emdash-cms/admin Patch
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/plugin-ai-moderation Patch
@emdash-cms/plugin-atproto Patch
@emdash-cms/plugin-audit-log Patch
@emdash-cms/plugin-color Patch
@emdash-cms/plugin-embeds Patch
@emdash-cms/plugin-forms Patch
@emdash-cms/plugin-webhook-notifier 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

@JULJERYT
Copy link
Copy Markdown
Contributor

JULJERYT commented Apr 6, 2026

i found some issues:

  • infinite fetch loop on mount
    (onLoadMore={() => void fetchNextPage()} creates a new function reference on every render)
  • effect fires during active search
  • test coverage gaps

@JULJERYT
Copy link
Copy Markdown
Contributor

JULJERYT commented Apr 6, 2026

and of course the changeset is missing 🫠

@JULJERYT
Copy link
Copy Markdown
Contributor

JULJERYT commented Apr 6, 2026

@ascorbic can you commit those changes?
packages/admin/src/router.tsx line 352

onLoadMore={React.useCallback(() => void fetchNextPage(), [fetchNextPage])}

packages/admin/src/components/ContentList.tsx lines 100-105

	// Auto-fetch next API page when user reaches the last client-side page.
	// skip when a search query is active
	// filteredItems shrinking would otherwise collapse totalPages to 1 and trigger a spurious fetch
	React.useEffect(() => {
		if (page >= totalPages - 1 && hasMore && onLoadMore && !searchQuery) {
			onLoadMore();
		}
	}, [page, totalPages, hasMore, onLoadMore, searchQuery]);

packages/admin/tests/components/ContentList.test.tsx between line 312-313

		it("auto-fetches when user navigates to the last client-side page", async () => {
			const onLoadMore = vi.fn();
			// 21 items = 2 pages of 20; user starts on page 0 (not the last page)
			const items = Array.from({ length: 21 }, (_, i) => makeItem({ id: `item_${i}` }));
			const screen = await render(
				<ContentList {...defaultProps} items={items} hasMore={true} onLoadMore={onLoadMore} />,
			);

			// On mount, page 0 is not the last page — no fetch yet
			expect(onLoadMore).not.toHaveBeenCalled();

			// Navigate to page 2 (the last page)
			await screen.getByRole("button", { name: "Next page" }).click();

			expect(onLoadMore).toHaveBeenCalledOnce();
		});

		it("does not auto-fetch when a search query is active", async () => {
			const onLoadMore = vi.fn();
			// 21 items so pagination exists, but search will collapse to 1 result / 1 page
			const items = [
				...Array.from({ length: 20 }, (_, i) => makeItem({ id: `item_${i}`, data: { title: `Post ${i}` } })),
				makeItem({ id: "unique", data: { title: "Unique Title" } }),
			];
			const screen = await render(
				<ContentList {...defaultProps} items={items} hasMore={true} onLoadMore={onLoadMore} />,
			);

			// No fetch on mount (page 0 is not the last page with 21 items)
			expect(onLoadMore).not.toHaveBeenCalled();

			// Search collapses results to 1 item — totalPages becomes 1, but should NOT fetch
			await screen.getByRole("searchbox").fill("Unique Title");

			expect(onLoadMore).not.toHaveBeenCalled();
		});

		it("shows '+' suffix on item count when hasMore is true and no search is active", async () => {
			const items = Array.from({ length: 21 }, (_, i) => makeItem({ id: `item_${i}` }));
			const screen = await render(
				<ContentList {...defaultProps} items={items} hasMore={true} />,
			);

			await expect.element(screen.getByText(/21\+ items/)).toBeInTheDocument();
		});

changeset

---
"@emdash-cms/admin": patch
---

Fix content list not fetching beyond the first API page when navigating to the last client-side page

- Add searchQuery guard to prevent spurious fetches when filtering
  collapses totalPages
- Stabilize onLoadMore with React.useCallback to avoid effect re-fires
- Add tests: navigation to last page triggers fetch, search suppresses
  fetch, "+" suffix on item count when hasMore is true
- Add changeset

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added size/M and removed size/S labels Apr 7, 2026
@all3f0r1
Copy link
Copy Markdown
Contributor Author

all3f0r1 commented Apr 7, 2026

@ascorbic I just applied the suggested changes from @JULJERYT.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 7, 2026

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@273

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@273

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@273

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@273

emdash

npm i https://pkg.pr.new/emdash@273

create-emdash

npm i https://pkg.pr.new/create-emdash@273

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@273

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@273

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@273

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@273

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@273

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@273

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@273

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@273

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@273

commit: 6c38d93

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Admin content list only shows first 50 items, no API-level pagination

2 participants