@elliemae/ds-pagination 3.70.0-next.3 → 3.70.0-next.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elliemae/ds-pagination",
|
|
3
|
-
"version": "3.70.0-next.
|
|
3
|
+
"version": "3.70.0-next.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "ICE MT - Dimsum - Pagination",
|
|
6
6
|
"files": [
|
|
7
|
-
"dist"
|
|
7
|
+
"dist",
|
|
8
|
+
"skills"
|
|
8
9
|
],
|
|
9
10
|
"module": "./dist/esm/index.js",
|
|
10
11
|
"main": "./dist/cjs/index.js",
|
|
@@ -37,22 +38,22 @@
|
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
40
|
"@xstyled/system": "~3.8.1",
|
|
40
|
-
"@elliemae/ds-
|
|
41
|
-
"@elliemae/ds-
|
|
42
|
-
"@elliemae/ds-dropdownmenu-v2": "3.70.0-next.
|
|
43
|
-
"@elliemae/ds-
|
|
44
|
-
"@elliemae/ds-
|
|
45
|
-
"@elliemae/ds-
|
|
46
|
-
"@elliemae/ds-
|
|
47
|
-
"@elliemae/ds-
|
|
48
|
-
"@elliemae/ds-
|
|
41
|
+
"@elliemae/ds-circular-progress-indicator": "3.70.0-next.4",
|
|
42
|
+
"@elliemae/ds-button-v2": "3.70.0-next.4",
|
|
43
|
+
"@elliemae/ds-dropdownmenu-v2": "3.70.0-next.4",
|
|
44
|
+
"@elliemae/ds-props-helpers": "3.70.0-next.4",
|
|
45
|
+
"@elliemae/ds-system": "3.70.0-next.4",
|
|
46
|
+
"@elliemae/ds-grid": "3.70.0-next.4",
|
|
47
|
+
"@elliemae/ds-icons": "3.70.0-next.4",
|
|
48
|
+
"@elliemae/ds-typography": "3.70.0-next.4",
|
|
49
|
+
"@elliemae/ds-typescript-helpers": "3.70.0-next.4"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"jest": "^30.0.0",
|
|
52
53
|
"styled-components": "~5.3.9",
|
|
53
54
|
"styled-system": "^5.1.5",
|
|
54
|
-
"@elliemae/ds-
|
|
55
|
-
"@elliemae/ds-
|
|
55
|
+
"@elliemae/ds-monorepo-devops": "3.70.0-next.4",
|
|
56
|
+
"@elliemae/ds-test-utils": "3.70.0-next.4"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
59
|
"@testing-library/react": "^16.0.1",
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ds-pagination-health-check
|
|
3
|
+
description: >
|
|
4
|
+
Audit the current state of a @elliemae/ds-pagination implementation. Use before starting
|
|
5
|
+
a task that depends on pagination, to verify a foundation before building on it, or when
|
|
6
|
+
diagnosing unexpected behavior. Produces a complete state report: what is correct and sound,
|
|
7
|
+
what requires fixing, what is suboptimal, and what gaps may affect the current task.
|
|
8
|
+
type: health-check
|
|
9
|
+
library: ds-pagination
|
|
10
|
+
library_version: '3.60.0'
|
|
11
|
+
requires:
|
|
12
|
+
- ds-pagination-setup
|
|
13
|
+
- ds-pagination-slots
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Purpose
|
|
17
|
+
|
|
18
|
+
This skill audits an existing `@elliemae/ds-pagination` integration. It is not a setup guide. It tells you the current state of a pagination implementation that already exists.
|
|
19
|
+
|
|
20
|
+
**Use it:**
|
|
21
|
+
|
|
22
|
+
- Before starting a task that involves pagination — to understand what you are about to touch
|
|
23
|
+
- Before making pagination load-bearing for a new feature — to verify the foundation
|
|
24
|
+
- When pagination is behaving unexpectedly — to surface what the implementation gets wrong
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Load the embedded knowledge
|
|
29
|
+
|
|
30
|
+
Load the following skills **as reference knowledge only**. You are reading their embedded understanding of correct patterns — their Common Mistakes, mandatory props, and OOB-first guidance become the diagnostic lens. You are not running them as tasks.
|
|
31
|
+
|
|
32
|
+
- `ds-pagination-setup` — required props, state management, server-side vs client-side model, deprecated API, pageIndex reset
|
|
33
|
+
- `ds-pagination-slots` — slot props have no effect on most pagination primitives in current version; standard HTML props work as the workaround
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Procedure
|
|
38
|
+
|
|
39
|
+
### Step 1 — Locate and read the implementation
|
|
40
|
+
|
|
41
|
+
Find every file where pagination primitives from `@elliemae/ds-pagination` are rendered. For each instance:
|
|
42
|
+
|
|
43
|
+
- Record which primitives are used: `DSPaginationContainer`, `DSPaginator`, `DSPagePrevButton`, `DSPageNextButton`, `DSPerPageSelector`, `DSPaginationSeparator`
|
|
44
|
+
- Record whether the monolithic `DSPagination` (deprecated) is used instead
|
|
45
|
+
- Read where pagination state lives (`pageIndex`, `pageSize`, `pageCount`) — state manager, store, atoms, or `useState`
|
|
46
|
+
- Read every callback handler (`onPageChange`, `onPreviousPage`, `onNextPage`, `onPageSizeChange`)
|
|
47
|
+
- Check whether `isLoadingPageCount` is used where `pageCount` may be async
|
|
48
|
+
- Check whether `TablePagination` (or equivalent) is defined at module level
|
|
49
|
+
- Check what `data` is passed to the consuming DataTable — full dataset or current page slice
|
|
50
|
+
|
|
51
|
+
### Step 2 — Apply diagnostic checks
|
|
52
|
+
|
|
53
|
+
**CRITICAL** — prevents correct function; fix before proceeding
|
|
54
|
+
**HIGH** — meaningful risk; should fix
|
|
55
|
+
**MEDIUM** — suboptimal; worth addressing
|
|
56
|
+
**SOUND** — correct; rely on it
|
|
57
|
+
|
|
58
|
+
#### Assembly checks
|
|
59
|
+
|
|
60
|
+
- [ ] Composable parts are used — not the deprecated monolithic `DSPagination` component
|
|
61
|
+
- [ ] `TablePagination` (or equivalent component) is defined at module level — not inside another component body
|
|
62
|
+
- [ ] `data` passed to any consuming DataTable is the current page slice — DataTable renders what it receives
|
|
63
|
+
|
|
64
|
+
#### State management checks
|
|
65
|
+
|
|
66
|
+
- [ ] `pageIndex`, `pageSize`, `pageCount` live in the project's state manager — not `useState`
|
|
67
|
+
- [ ] No `useEffect` watching pagination state to dispatch API calls — consequences belong inside the callbacks
|
|
68
|
+
- [ ] `pageIndex` resets to 1 inside `onPageSizeChange` — not doing so leaves users on a non-existent page
|
|
69
|
+
|
|
70
|
+
#### Loading state checks
|
|
71
|
+
|
|
72
|
+
- [ ] `isLoadingPageCount={true}` is used when `pageCount` is async — absence renders `0` or broken state
|
|
73
|
+
- [ ] `isLoadingPageCount` and `pageCount ?? 0` are treated as independent concerns — the `?? 0` is a null-safety guard, not a spinner trigger
|
|
74
|
+
|
|
75
|
+
#### Memoization checks
|
|
76
|
+
|
|
77
|
+
- [ ] `onPageChange`, `onPreviousPage`, `onNextPage`, `onPageSizeChange` are wrapped in `useCallback`
|
|
78
|
+
- [ ] `perPageOptions` is stable (`useMemo` if constructed inline)
|
|
79
|
+
- [ ] `pageDetails` is stable (`useMemo` if constructed inline)
|
|
80
|
+
|
|
81
|
+
#### Deprecated API check
|
|
82
|
+
|
|
83
|
+
- [ ] Capital-P `Pagination` render prop is used where DataTable integration is present — not deprecated lowercase `pagination` prop
|
|
84
|
+
|
|
85
|
+
#### Pagination model check
|
|
86
|
+
|
|
87
|
+
- [ ] Every page or size change dispatches an API call — client-side array slicing is only acceptable as a temporary, explicitly owned workaround with a documented remediation plan
|
|
88
|
+
|
|
89
|
+
### Step 3 — Produce the state report
|
|
90
|
+
|
|
91
|
+
**Foundation — what is correct and sound**
|
|
92
|
+
What the implementation gets right. Be specific — this is what you can rely on.
|
|
93
|
+
|
|
94
|
+
**Critical issues — fix before proceeding**
|
|
95
|
+
Each item with file, location, and pointer to the `ds-pagination-setup` skill section covering the fix.
|
|
96
|
+
|
|
97
|
+
**Warnings — should address, not blocking**
|
|
98
|
+
Each item with context on the risk.
|
|
99
|
+
|
|
100
|
+
**Awareness — no action needed, good to know**
|
|
101
|
+
Suboptimal patterns that are not breaking.
|
|
102
|
+
|
|
103
|
+
**Gaps relevant to this task**
|
|
104
|
+
What the current task will require that is not yet present in the implementation.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## What this skill does NOT do
|
|
109
|
+
|
|
110
|
+
- It does not generate or modify code
|
|
111
|
+
- It does not run the setup skill as a task
|
|
112
|
+
- It does not replace human judgment on trade-offs
|
|
113
|
+
|
|
114
|
+
## When this isn't enough
|
|
115
|
+
|
|
116
|
+
**ICE internal:** Microsoft Teams — Dimsum channel (informal) / Jira Dimsum board (formal)
|
|
117
|
+
**Partners:** Your organization's Dimsum point of contact
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ds-pagination-setup
|
|
3
|
+
description: >
|
|
4
|
+
Composable pagination bar assembly for @elliemae/ds-pagination. DSPaginationContainer,
|
|
5
|
+
DSPaginator, DSPagePrevButton, DSPageNextButton, DSPerPageSelector, DSPaginationSeparator.
|
|
6
|
+
State (pageIndex, pageSize, pageCount) in project state manager — not useState. isLoadingPageCount
|
|
7
|
+
on DSPaginator for async page count. DSPagination (monolithic) is the legacy API — never use.
|
|
8
|
+
pageIndex resets to 1 on pageSize change.
|
|
9
|
+
type: core
|
|
10
|
+
library: ds-pagination
|
|
11
|
+
library_version: '3.60.0'
|
|
12
|
+
sources:
|
|
13
|
+
- '@elliemae/ds-pagination:dist/types/react-desc-prop-types.d.ts'
|
|
14
|
+
- '@elliemae/ds-pagination:dist/types/parts/DSPaginator/react-desc-prop-types.d.ts'
|
|
15
|
+
- '@elliemae/ds-pagination:dist/types/parts/DSPerPageSelector/react-desc-prop-types.d.ts'
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Setup
|
|
19
|
+
|
|
20
|
+
Define `PaginationBar` at module level — never inside another component. Read pagination state from the project's state manager. The most common stacks in ICE codebases, in order of prevalence: RTK (Redux Toolkit), vanilla Redux, Redux-Saga (pre-RTK codebases), and other solutions that need to be discovered before writing any state shape.
|
|
21
|
+
|
|
22
|
+
**On pagination model — ideal vs current reality:** The correct long-term model is server-side pagination: every page or size change dispatches an API call, and the backend returns only the requested slice. This is how `DSPagination` is meant to be used. If the current backend does not yet support paginated endpoints and an in-memory slice is the temporary reality, that is a legitimate constraint — not something to pretend away. If that is the situation: surface it explicitly, confirm the team understands the tradeoff, verify there is a documented remediation plan to move to server-side when backend support is available, and proceed. A deliberate, owned compromise is different from an unexamined shortcut. The example below shows the server-side model — if adapting to an in-memory slice, the `fetchPage` dispatches become local slice operations, and the team explicitly owns the data freshness and memory cost implications.
|
|
23
|
+
|
|
24
|
+
```jsx
|
|
25
|
+
import {
|
|
26
|
+
DSPageNextButton,
|
|
27
|
+
DSPagePrevButton,
|
|
28
|
+
DSPaginationContainer,
|
|
29
|
+
DSPaginationSeparator,
|
|
30
|
+
DSPaginator,
|
|
31
|
+
DSPerPageSelector,
|
|
32
|
+
} from '@elliemae/ds-pagination';
|
|
33
|
+
|
|
34
|
+
const PaginationBar = () => {
|
|
35
|
+
const { pageIndex, pageSize, pageCount } = useSelector(selectPaginationState);
|
|
36
|
+
const dispatch = useDispatch();
|
|
37
|
+
|
|
38
|
+
const onPreviousPage = useCallback(() => {
|
|
39
|
+
dispatch(fetchPage({ page: pageIndex - 1, pageSize }));
|
|
40
|
+
}, [dispatch, pageIndex, pageSize]);
|
|
41
|
+
|
|
42
|
+
const onNextPage = useCallback(() => {
|
|
43
|
+
dispatch(fetchPage({ page: pageIndex + 1, pageSize }));
|
|
44
|
+
}, [dispatch, pageIndex, pageSize]);
|
|
45
|
+
|
|
46
|
+
const onPageChange = useCallback(
|
|
47
|
+
(idx) => {
|
|
48
|
+
dispatch(fetchPage({ page: idx, pageSize }));
|
|
49
|
+
},
|
|
50
|
+
[dispatch, pageSize],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const onPageSizeChange = useCallback(
|
|
54
|
+
(newPageSize) => {
|
|
55
|
+
dispatch(setPageSize(newPageSize));
|
|
56
|
+
dispatch(setPageIndex(1));
|
|
57
|
+
dispatch(fetchPage({ page: 1, pageSize: newPageSize }));
|
|
58
|
+
},
|
|
59
|
+
[dispatch],
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<DSPaginationContainer>
|
|
64
|
+
<DSPerPageSelector pageSize={pageSize} onPageSizeChange={onPageSizeChange} />
|
|
65
|
+
<DSPaginationSeparator mr={24} />
|
|
66
|
+
<DSPagePrevButton canPreviousPage={pageIndex !== 1} onPreviousPage={onPreviousPage} />
|
|
67
|
+
<DSPaginationSeparator />
|
|
68
|
+
<DSPaginator pageCount={pageCount} pageIndex={pageIndex} onPageChange={onPageChange} />
|
|
69
|
+
<DSPaginationSeparator />
|
|
70
|
+
<DSPageNextButton canNextPage={pageIndex !== pageCount} onNextPage={onNextPage} />
|
|
71
|
+
</DSPaginationContainer>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Core Patterns
|
|
77
|
+
|
|
78
|
+
### Basic prev/page/next bar (no per-page selector)
|
|
79
|
+
|
|
80
|
+
```jsx
|
|
81
|
+
const PaginationBar = () => {
|
|
82
|
+
const { pageIndex, pageCount } = useSelector(selectPaginationState);
|
|
83
|
+
const dispatch = useDispatch();
|
|
84
|
+
const onPreviousPage = useCallback(() => dispatch(fetchPage({ page: pageIndex - 1 })), [dispatch, pageIndex]);
|
|
85
|
+
const onPageChange = useCallback((idx) => dispatch(fetchPage({ page: idx })), [dispatch]);
|
|
86
|
+
const onNextPage = useCallback(() => dispatch(fetchPage({ page: pageIndex + 1 })), [dispatch, pageIndex]);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<DSPaginationContainer>
|
|
90
|
+
<DSPagePrevButton canPreviousPage={pageIndex !== 1} onPreviousPage={onPreviousPage} />
|
|
91
|
+
<DSPaginationSeparator />
|
|
92
|
+
<DSPaginator pageCount={pageCount} pageIndex={pageIndex} onPageChange={onPageChange} />
|
|
93
|
+
<DSPaginationSeparator />
|
|
94
|
+
<DSPageNextButton canNextPage={pageIndex !== pageCount} onNextPage={onNextPage} />
|
|
95
|
+
</DSPaginationContainer>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Async page count with isLoadingPageCount
|
|
101
|
+
|
|
102
|
+
`isLoadingPageCount={true}` shows a spinner in `DSPaginator` instead of the page picker. While loading, also guard `canPreviousPage` and `canNextPage` — the total page count is unknown so navigation should be disabled.
|
|
103
|
+
|
|
104
|
+
```jsx
|
|
105
|
+
<DSPagePrevButton canPreviousPage={!isPageCountLoading && pageIndex !== 1} onPreviousPage={onPreviousPage} />
|
|
106
|
+
<DSPaginationSeparator />
|
|
107
|
+
<DSPaginator
|
|
108
|
+
pageCount={pageCount}
|
|
109
|
+
isLoadingPageCount={isPageCountLoading}
|
|
110
|
+
pageIndex={pageIndex}
|
|
111
|
+
onPageChange={onPageChange}
|
|
112
|
+
/>
|
|
113
|
+
<DSPaginationSeparator />
|
|
114
|
+
<DSPageNextButton canNextPage={!isPageCountLoading && pageIndex !== pageCount} onNextPage={onNextPage} />
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
If `pageCount` in your state can be `null` or `undefined` while loading, add a fallback: `pageCount={pageCount ?? 0}`. This prevents passing an invalid type — it is a null-safety guard unrelated to `isLoadingPageCount`.
|
|
118
|
+
|
|
119
|
+
### Per-page selector — options array vs step-based
|
|
120
|
+
|
|
121
|
+
Two configuration modes for `DSPerPageSelector`:
|
|
122
|
+
|
|
123
|
+
```jsx
|
|
124
|
+
// Explicit options array — use when you need specific values including non-numeric options
|
|
125
|
+
<DSPerPageSelector
|
|
126
|
+
pageSize={pageSize}
|
|
127
|
+
onPageSizeChange={onPageSizeChange}
|
|
128
|
+
perPageOptions={[10, 20, 50, { type: 'single', dsId: 'all', label: 'All', value: totalRecords }]}
|
|
129
|
+
/>
|
|
130
|
+
|
|
131
|
+
// Step-based — use when you want a generated range from minPerPage to maxPerPage
|
|
132
|
+
<DSPerPageSelector
|
|
133
|
+
pageSize={pageSize}
|
|
134
|
+
onPageSizeChange={onPageSizeChange}
|
|
135
|
+
minPerPage={10}
|
|
136
|
+
maxPerPage={50}
|
|
137
|
+
perPageStep={10}
|
|
138
|
+
/>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Both modes require `onPageSizeChange` to reset `pageIndex` to 1.
|
|
142
|
+
|
|
143
|
+
### Segmented paginator with pageDetails
|
|
144
|
+
|
|
145
|
+
`pageDetails` provides per-page range summaries in the paginator dropdown. `pageDetailsTitle` labels the column. Build server-side or derive from dataset slice metadata.
|
|
146
|
+
|
|
147
|
+
```jsx
|
|
148
|
+
const pageDetails = pages.map((p) => `${p.firstRecord}-${p.lastRecord}`);
|
|
149
|
+
|
|
150
|
+
<DSPaginator
|
|
151
|
+
pageCount={pageCount}
|
|
152
|
+
pageIndex={pageIndex}
|
|
153
|
+
onPageChange={onPageChange}
|
|
154
|
+
pageDetailsTitle="Records"
|
|
155
|
+
pageDetails={pageDetails}
|
|
156
|
+
/>;
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Common Mistakes
|
|
160
|
+
|
|
161
|
+
### CRITICAL Using DSPagination (monolithic) instead of composable parts
|
|
162
|
+
|
|
163
|
+
Wrong:
|
|
164
|
+
|
|
165
|
+
```jsx
|
|
166
|
+
import { DSPagination } from '@elliemae/ds-pagination';
|
|
167
|
+
<DSPagination pageIndex={pageIndex} pageCount={pageCount} onPageChange={handlePageChange} />;
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Correct:
|
|
171
|
+
|
|
172
|
+
```jsx
|
|
173
|
+
import {
|
|
174
|
+
DSPaginationContainer,
|
|
175
|
+
DSPaginator,
|
|
176
|
+
DSPagePrevButton,
|
|
177
|
+
DSPageNextButton,
|
|
178
|
+
DSPaginationSeparator,
|
|
179
|
+
} from '@elliemae/ds-pagination';
|
|
180
|
+
|
|
181
|
+
<DSPaginationContainer>
|
|
182
|
+
<DSPagePrevButton canPreviousPage={pageIndex !== 1} onPreviousPage={handlePrevious} />
|
|
183
|
+
<DSPaginationSeparator />
|
|
184
|
+
<DSPaginator pageCount={pageCount} pageIndex={pageIndex} onPageChange={handlePageChange} />
|
|
185
|
+
<DSPaginationSeparator />
|
|
186
|
+
<DSPageNextButton canNextPage={pageIndex !== pageCount} onNextPage={handleNext} />
|
|
187
|
+
</DSPaginationContainer>;
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`DSPagination` is the pre-v3.15 monolithic API, kept only for backward compatibility — its namespace is marked `// LEGACY WAY OF USING THE PAGINATION COMPONENT`. All new implementations use the composable parts.
|
|
191
|
+
|
|
192
|
+
Source: https://dimsum.mortgagetech.ice.com/iframe.html?id=components-pagination-features--basic
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
### HIGH Not resetting pageIndex to 1 when pageSize changes
|
|
197
|
+
|
|
198
|
+
Wrong:
|
|
199
|
+
|
|
200
|
+
```jsx
|
|
201
|
+
const onPageSizeChange = useCallback(
|
|
202
|
+
(newPageSize) => {
|
|
203
|
+
dispatch(setPageSize(newPageSize));
|
|
204
|
+
dispatch(fetchPage({ page: pageIndex, pageSize: newPageSize }));
|
|
205
|
+
},
|
|
206
|
+
[dispatch, pageIndex, pageSize],
|
|
207
|
+
);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Correct:
|
|
211
|
+
|
|
212
|
+
```jsx
|
|
213
|
+
const onPageSizeChange = useCallback(
|
|
214
|
+
(newPageSize) => {
|
|
215
|
+
dispatch(setPageSize(newPageSize));
|
|
216
|
+
dispatch(setPageIndex(1));
|
|
217
|
+
dispatch(fetchPage({ page: 1, pageSize: newPageSize }));
|
|
218
|
+
},
|
|
219
|
+
[dispatch],
|
|
220
|
+
);
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
When page size changes, the total page count changes. The current `pageIndex` may no longer exist in the new page layout.
|
|
224
|
+
|
|
225
|
+
Source: https://dimsum.mortgagetech.ice.com/iframe.html?id=components-pagination-features--per-page-selector-with-options
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
### HIGH Non-primitive pagination props not memoized
|
|
230
|
+
|
|
231
|
+
Every callback and non-primitive prop passed to pagination primitives must be a stable reference.
|
|
232
|
+
|
|
233
|
+
| Prop | Component | Memoization |
|
|
234
|
+
| ------------------ | ------------------- | ------------------------------- |
|
|
235
|
+
| `onPreviousPage` | `DSPagePrevButton` | `useCallback` |
|
|
236
|
+
| `onNextPage` | `DSPageNextButton` | `useCallback` |
|
|
237
|
+
| `onPageChange` | `DSPaginator` | `useCallback` |
|
|
238
|
+
| `onPageSizeChange` | `DSPerPageSelector` | `useCallback` |
|
|
239
|
+
| `perPageOptions` | `DSPerPageSelector` | `useMemo` if constructed inline |
|
|
240
|
+
| `pageDetails` | `DSPaginator` | `useMemo` if constructed inline |
|
|
241
|
+
|
|
242
|
+
Source: https://dimsum.mortgagetech.ice.com/iframe.html?id=components-pagination-features--basic
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
### MEDIUM Passing 0 for pageCount while total count is loading
|
|
247
|
+
|
|
248
|
+
Wrong:
|
|
249
|
+
|
|
250
|
+
```jsx
|
|
251
|
+
<DSPaginator pageCount={0} pageIndex={pageIndex} onPageChange={onPageChange} />
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Correct:
|
|
255
|
+
|
|
256
|
+
```jsx
|
|
257
|
+
<DSPaginator
|
|
258
|
+
pageCount={pageCount}
|
|
259
|
+
isLoadingPageCount={isPageCountLoading}
|
|
260
|
+
pageIndex={pageIndex}
|
|
261
|
+
onPageChange={onPageChange}
|
|
262
|
+
/>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
`isLoadingPageCount={true}` shows the spinner regardless of `pageCount`. Without it, the paginator renders whatever value `pageCount` has — including `0`, which displays as a broken state.
|
|
266
|
+
|
|
267
|
+
Source: https://dimsum.mortgagetech.ice.com/iframe.html?id=components-pagination-features--loading-page-count
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
### HIGH Client-side array slicing used without deliberate team ownership
|
|
272
|
+
|
|
273
|
+
```jsx
|
|
274
|
+
const pagedData = allRecords.slice((pageIndex - 1) * pageSize, pageIndex * pageSize);
|
|
275
|
+
const pageCount = Math.ceil(allRecords.length / pageSize);
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Client-side slicing is not automatically wrong — it is a known tradeoff that is sometimes the right call given backend constraints. What makes it a problem is when it is implemented without explicit team awareness: the full dataset is in memory and growing stale the moment the source data changes, there is no backend cost saving, and compounding with sorting and filtering multiplies the fragility.
|
|
279
|
+
|
|
280
|
+
Before implementing or accepting a client-side slice approach, surface these questions to the team:
|
|
281
|
+
|
|
282
|
+
- Does the backend currently support paginated endpoints? If not, is this on a roadmap?
|
|
283
|
+
- Has the team acknowledged the data freshness implication (in-memory snapshot vs live data)?
|
|
284
|
+
- Is there a documented plan to migrate to server-side pagination when backend support is available?
|
|
285
|
+
|
|
286
|
+
If the answer to all three is yes and the team owns the decision — proceed. Document the temporary nature explicitly in code. If the team has not considered these questions, raise them before writing the implementation.
|
|
287
|
+
|
|
288
|
+
Source: https://dimsum.mortgagetech.ice.com/iframe.html?id=components-pagination-features--basic
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
### HIGH Managing pagination state with useState instead of the project state manager
|
|
293
|
+
|
|
294
|
+
Wrong:
|
|
295
|
+
|
|
296
|
+
```jsx
|
|
297
|
+
const [pageIndex, setPageIndex] = useState(1);
|
|
298
|
+
const [pageSize, setPageSize] = useState(10);
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Correct:
|
|
302
|
+
|
|
303
|
+
```jsx
|
|
304
|
+
const { pageIndex, pageSize, pageCount } = useSelector(selectPaginationState);
|
|
305
|
+
const dispatch = useDispatch();
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Stories use `useState` as a teaching simplification. Production code must use the project's state manager. Pagination state drives API calls and is shared across the view — it cannot be local component state.
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## When composable parts are not enough
|
|
313
|
+
|
|
314
|
+
If the six composable primitives cannot satisfy the layout or behavior requirement:
|
|
315
|
+
|
|
316
|
+
1. Confirm the requirement with UI/UX — most pagination variations are achievable via composition, ordering, and xstyled props on the primitives.
|
|
317
|
+
2. Contact the Dimsum team to validate whether a first-class solution should be added.
|
|
318
|
+
|
|
319
|
+
**ICE internal:** Microsoft Teams — Dimsum channel (informal) / Jira Dimsum board (formal)
|
|
320
|
+
**Partners:** Your organization's Dimsum point of contact
|
|
321
|
+
|
|
322
|
+
See also: `@elliemae/ds-data-table` `Pagination` render prop — if this pagination bar is used with a DataTable, load the DataTable pagination skill for the integration wiring.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ds-pagination-slots
|
|
3
|
+
description: >
|
|
4
|
+
Slot injection surface for @elliemae/ds-pagination. Verified against v3.60.0 via probe.
|
|
5
|
+
DSPaginationContainer (ROOT, WRAPPER) and DSPaginationSeparator (SEPARATOR) have functional
|
|
6
|
+
slot injection. DSPaginator, DSPagePrevButton, DSPageNextButton, and DSPerPageSelector slot
|
|
7
|
+
props have no effect in the current version — use standard HTML props on those four directly.
|
|
8
|
+
type: core
|
|
9
|
+
library: ds-pagination
|
|
10
|
+
library_version: '3.60.0'
|
|
11
|
+
requires:
|
|
12
|
+
- ds-pagination-setup
|
|
13
|
+
sources:
|
|
14
|
+
- '@elliemae/ds-pagination:dist/types/react-desc-prop-types.d.ts'
|
|
15
|
+
- '@elliemae/ds-pagination:dist/types/parts/DSPaginationContainer/react-desc-prop-types.d.ts'
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Slot surface — DOM hierarchy
|
|
19
|
+
|
|
20
|
+
Slots are injection points into specific rendered DOM nodes. Understanding the hierarchy is required to inject props at the correct level.
|
|
21
|
+
|
|
22
|
+
**About this map:** Verified against `@elliemae/ds-pagination@3.60.0` via probe tests. Slot names are governed by the Dimsum breaking change protocol — renames are intentional and documented. If something doesn't match your installed version, inspect the rendered HTML in your environment. If you cannot do it autonomously, ask the human in the loop to render the component with synthetic data only — not production data containing real user information.
|
|
23
|
+
|
|
24
|
+
Each entry shows: `slot-key (data-dimsum-slot="value") · tag[role] · notes`.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
DSPaginationContainer renders:
|
|
28
|
+
|
|
29
|
+
ROOT (dsPaginationRoot) · div ← inject aria-label, data-* here
|
|
30
|
+
└── WRAPPER (dsPaginationWrapper) · div
|
|
31
|
+
├── DSPerPageSelector ← no dsPaginationPerPageSelector in DOM (slot prop not active in current version)
|
|
32
|
+
│ └── dsButtonRoot · button (inner dropdown trigger)
|
|
33
|
+
│ └── dsTypographyRoot · span
|
|
34
|
+
├── SEPARATOR (dsPaginationSeparator) · div ← functional ✓
|
|
35
|
+
├── DSPagePrevButton ← no dsPaginationPrevPage in DOM (slot prop not active in current version)
|
|
36
|
+
│ └── dsButtonRoot · button (inner button)
|
|
37
|
+
│ └── dsIconRoot · span
|
|
38
|
+
│ └── dsIconSvg · svg
|
|
39
|
+
├── SEPARATOR (dsPaginationSeparator) · div ← functional ✓
|
|
40
|
+
├── DSPaginator ← no dsPaginationPaginator in DOM (slot prop not active in current version)
|
|
41
|
+
│ └── dsButtonRoot · button (inner dropdown trigger)
|
|
42
|
+
│ ├── dsTypographyRoot · span
|
|
43
|
+
│ └── dsIconRoot · span
|
|
44
|
+
│ └── dsIconSvg · svg
|
|
45
|
+
├── SEPARATOR (dsPaginationSeparator) · div ← functional ✓
|
|
46
|
+
└── DSPageNextButton ← no dsPaginationNextButton in DOM (slot prop not active in current version)
|
|
47
|
+
└── dsButtonRoot · button (inner button)
|
|
48
|
+
└── dsIconRoot · span
|
|
49
|
+
└── dsIconSvg · svg
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### What works
|
|
53
|
+
|
|
54
|
+
`DSPaginationContainer` slot props (`dsPaginationRoot`, `dsPaginationWrapper`) accept injection:
|
|
55
|
+
|
|
56
|
+
```jsx
|
|
57
|
+
<DSPaginationContainer dsPaginationRoot={{ 'aria-label': 'Loan results pagination', 'data-section': 'pagination' }}>
|
|
58
|
+
...
|
|
59
|
+
</DSPaginationContainer>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`DSPaginationSeparator` slot prop (`dsPaginationSeparator`) also works.
|
|
63
|
+
|
|
64
|
+
### What doesn't work
|
|
65
|
+
|
|
66
|
+
`DSPaginator`, `DSPagePrevButton`, `DSPageNextButton`, and `DSPerPageSelector` do not produce their own `dsPagination*` slot attributes. Passing e.g. `dsPaginationPaginator={{ ... }}` to `DSPaginator` has no effect at runtime.
|
|
67
|
+
|
|
68
|
+
**Current workaround:** Use standard HTML props directly on these components — `aria-*` and `data-*` attributes passed directly as props work correctly on all of them:
|
|
69
|
+
|
|
70
|
+
```jsx
|
|
71
|
+
<DSPagePrevButton
|
|
72
|
+
canPreviousPage={pageIndex !== 1}
|
|
73
|
+
onPreviousPage={onPreviousPage}
|
|
74
|
+
aria-label="Go to previous page"
|
|
75
|
+
data-testid="pagination-prev"
|
|
76
|
+
/>
|
|
77
|
+
|
|
78
|
+
<DSPaginator
|
|
79
|
+
pageCount={pageCount}
|
|
80
|
+
pageIndex={pageIndex}
|
|
81
|
+
onPageChange={onPageChange}
|
|
82
|
+
aria-label="Page selector"
|
|
83
|
+
/>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This gap is tracked by the Dimsum team. Contact them if slot injection on these components is required for your use case.
|
|
87
|
+
|
|
88
|
+
## When this isn't enough
|
|
89
|
+
|
|
90
|
+
**ICE internal:** Microsoft Teams — Dimsum channel (informal) / Jira Dimsum board (formal)
|
|
91
|
+
**Partners:** Your organization's Dimsum point of contact
|