Skip to content

Commit 437d9b8

Browse files
committed
feat: pagination phase 5 fix
1 parent c97a457 commit 437d9b8

File tree

4 files changed

+44
-33
lines changed

4 files changed

+44
-33
lines changed

docs/PAGINATION_IMPLEMENTATION_PLAN.md

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
https://github.com/decaporg/decap-cms/issues/3714
55

66
## Overview
7-
Add pagination support to large collections in Decap CMS. Pagination will be opt-in per collection or globally configurable, starting with GitHub and Git Gateway backends.
7+
Add pagination support to large collections in Decap CMS. Pagination will be opt-in per collection or globally. It will work local, GitHub, and Git Gateway backends.
88

99
## Current State Analysis
1010

@@ -80,8 +80,8 @@ collections:
8080
folder: content/products
8181
# Detailed configuration
8282
pagination:
83-
per_page: 50
84-
user_options: [25, 50, 100, 200]
83+
per_page: 50 # default
84+
user_options: [25, 50, 100, 200] # default is empty array, means no user options
8585
```
8686
8787
### Global Configuration
@@ -103,20 +103,11 @@ collections:
103103
104104
### TypeScript Types
105105
```typescript
106-
// In CmsCollection interface
106+
// In CmsCollection interface and Global config
107107
pagination?: boolean | {
108+
enabled?: boolean; // Enable pagination (default: true)
108109
per_page?: number; // Default items per page (default: 100)
109-
user_options?: number[] | false; // User-selectable options (default: [25, 50, 100, 200])
110-
}
111-
112-
// Global config
113-
interface CmsConfig {
114-
// ... existing fields
115-
pagination?: {
116-
enabled?: boolean;
117-
per_page?: number;
118-
user_options?: number[] | false;
119-
}
110+
user_options?: number[] | false; // User-selectable options (default: [])
120111
}
121112
```
122113

@@ -131,11 +122,10 @@ interface CmsConfig {
131122
3. Create configuration parser/normalizer:
132123
- Merge global and per-collection settings
133124
- Normalize boolean to full config object
134-
- Set defaults: `per_page: 100`, `user_options: [25, 50, 100, 200]`
125+
- Set defaults: `per_page: 50`, `user_options: []`
135126
4. Add helper functions:
136127
- `isPaginationEnabled(collection, globalConfig): boolean`
137128
- `getPaginationConfig(collection, globalConfig): PaginationConfig`
138-
5. Add unit tests for configuration parsing
139129

140130
**Files to Create:**
141131
- `/packages/decap-cms-core/src/lib/pagination.ts` - Pagination utilities
@@ -144,8 +134,8 @@ interface CmsConfig {
144134
- `/packages/decap-cms-core/src/types/redux.ts`
145135
- `/packages/decap-cms-core/src/reducers/config.ts` (if needed for validation)
146136

147-
### Phase 2: Backend Implementation (GitHub)
148-
**Goal:** Make GitHub backend return paginated results based on config
137+
### Phase 2: Backend Implementation (local, GitHub)
138+
**Goal:** Make GitHub and local backend return paginated results based on config
149139

150140
**Tasks:**
151141
1. Modify `GitHub.entriesByFolder()` to:
@@ -168,7 +158,7 @@ interface CmsConfig {
168158
**Tasks:**
169159
1. Update `GitGateway.entriesByFolder()` to pass pagination params
170160
2. Ensure cursor is properly forwarded
171-
3. Test with both GitHub and GitLab backends
161+
3. Test with local, GitHub and GitLab backends
172162

173163
**Files to Modify:**
174164
- `/packages/decap-cms-backend-git-gateway/src/implementation.ts`
@@ -208,10 +198,9 @@ interface CmsConfig {
208198
- Page number display (e.g., "Page 2 of 45")
209199
- Previous/Next buttons
210200
- First/Last buttons (optional)
211-
- Items per page selector (if `user_options` is not false)
212-
- Jump to page input (optional for later)
201+
- Items per page selector (if `user_options` is not false or empty)
213202
2. Integrate pagination into `EntriesCollection`:
214-
- Show pagination at top and/or bottom
203+
- Show pagination at the bottom
215204
- Handle page change events
216205
- Show loading state during page transitions
217206
3. Update `Entries` component:
@@ -223,7 +212,7 @@ interface CmsConfig {
223212
- Sorting
224213
- Filtering
225214
- Search
226-
7. Add localization strings
215+
7. Add localization strings (only English for now)
227216

228217
**Files to Modify:**
229218
- `/packages/decap-cms-core/src/components/Collection/Entries/EntriesCollection.js`

packages/decap-cms-backend-github/src/implementation.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,10 +442,10 @@ export default class GitHub implements Implementation {
442442
cursor = result.cursor;
443443
return result.files;
444444
} else {
445-
// Non-paginated: return all files with a cursor for the first page
445+
// Non-paginated: return all files (no slicing)
446446
const result = this.getCursorAndFiles(filtered, 1, pageSize);
447447
cursor = result.cursor;
448-
return result.files;
448+
return filtered;
449449
}
450450
});
451451

packages/decap-cms-backend-test/src/implementation.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,15 @@ function deleteFile(path: string, tree: RepoTree) {
8080
unset(tree, path.split('/'));
8181
}
8282

83-
const pageSize = 10;
83+
const DEFAULT_PAGE_SIZE = 10;
8484

8585
function getCursor(
8686
folder: string,
8787
extension: string,
8888
entries: ImplementationEntry[],
8989
index: number,
9090
depth: number,
91+
pageSize = DEFAULT_PAGE_SIZE,
9192
) {
9293
const count = entries.length;
9394
const pageCount = Math.floor(count / pageSize);
@@ -173,6 +174,9 @@ export default class TestBackend implements Implementation {
173174
pageCount: number;
174175
depth: number;
175176
};
177+
const meta = cursor.meta!;
178+
const currentPageSize = meta.get('pageSize', DEFAULT_PAGE_SIZE);
179+
176180
const newIndex = (() => {
177181
if (action === 'next') {
178182
return (index as number) + 1;
@@ -194,19 +198,37 @@ export default class TestBackend implements Implementation {
194198
data: f.content as string,
195199
file: { path: f.path, id: f.path },
196200
}));
197-
const entries = allEntries.slice(newIndex * pageSize, newIndex * pageSize + pageSize);
198-
const newCursor = getCursor(folder, extension, allEntries, newIndex, depth);
201+
const entries = allEntries.slice(newIndex * currentPageSize, newIndex * currentPageSize + currentPageSize);
202+
const newCursor = getCursor(folder, extension, allEntries, newIndex, depth, currentPageSize);
199203
return Promise.resolve({ entries, cursor: newCursor });
200204
}
201205

202-
entriesByFolder(folder: string, extension: string, depth: number) {
206+
entriesByFolder(
207+
folder: string,
208+
extension: string,
209+
depth: number,
210+
options?: {
211+
page?: number;
212+
pageSize?: number;
213+
pagination?: boolean;
214+
},
215+
) {
203216
const files = folder ? getFolderFiles(window.repoFiles, folder, extension, depth) : [];
204217
const entries = files.map(f => ({
205218
data: f.content as string,
206219
file: { path: f.path, id: f.path },
207220
}));
208-
const cursor = getCursor(folder, extension, entries, 0, depth);
209-
const ret = take(entries, pageSize);
221+
const pageSize = options?.pageSize ?? DEFAULT_PAGE_SIZE;
222+
const page = options?.page ?? 1;
223+
const usePagination = options?.pagination ?? false;
224+
225+
const cursor = getCursor(folder, extension, entries, page - 1, depth, pageSize);
226+
227+
// If pagination is enabled, return only the requested page
228+
// Otherwise, return all entries (for backward compatibility)
229+
const ret = usePagination
230+
? take(entries.slice((page - 1) * pageSize), pageSize)
231+
: entries;
210232
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
211233
// @ts-ignore
212234
ret[CURSOR_COMPATIBILITY_SYMBOL] = cursor;

packages/decap-cms-core/src/lib/pagination.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import type { CmsCollection, CmsConfig, PaginationConfig, Collection } from '../types/redux';
33

44
const DEFAULT_PER_PAGE = 100;
5-
const DEFAULT_USER_OPTIONS = [25, 50, 100, 250, 500];
5+
const DEFAULT_USER_OPTIONS: number[] = [];
66

77
type CollectionLike = CmsCollection | Collection;
88

0 commit comments

Comments
 (0)