@lowdefy/codemods 0.0.0-experimental-20260420141318 → 5.1.0
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
package/registry.json
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"versions": [
|
|
3
|
+
{
|
|
4
|
+
"version": "5.1.0",
|
|
5
|
+
"from": ">=5.0.0 <5.1.0",
|
|
6
|
+
"description": "AgGrid built-in cell renderer types (tag, avatar, link, date, boolean, progress, number) + ellipsis + align",
|
|
7
|
+
"codemods": [
|
|
8
|
+
{
|
|
9
|
+
"id": "aggrid-cell-types-migration",
|
|
10
|
+
"description": "Report-only: detect cellRenderer HTML patterns that could migrate to cell.type. Each column requires human review and author sign-off.",
|
|
11
|
+
"path": "v5-1-0/01-aggrid-cell-types-migration.md"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
},
|
|
3
15
|
{
|
|
4
16
|
"version": "5.0.0",
|
|
5
17
|
"from": ">=4.0.0 <5.0.0",
|
|
@@ -12,18 +12,18 @@ Moment.js locale shortcuts (`l`, `ll`, `lll`, `llll` and uppercase `L`, `LL`, `L
|
|
|
12
12
|
|
|
13
13
|
**Replace these with explicit dayjs format tokens:**
|
|
14
14
|
|
|
15
|
-
| Moment shortcut | Moment output example
|
|
16
|
-
|
|
|
17
|
-
| `l`
|
|
18
|
-
| `ll`
|
|
19
|
-
| `lll`
|
|
20
|
-
| `llll`
|
|
21
|
-
| `L`
|
|
22
|
-
| `LL`
|
|
23
|
-
| `LLL`
|
|
24
|
-
| `LLLL`
|
|
25
|
-
| `LT`
|
|
26
|
-
| `LTS`
|
|
15
|
+
| Moment shortcut | Moment output example | Replacement |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `l` | 3/26/2026 | `D/M/YYYY` |
|
|
18
|
+
| `ll` | Mar 26, 2026 | `D MMM YYYY` |
|
|
19
|
+
| `lll` | Mar 26, 2026 3:27 PM | `D MMM YYYY h:mm A` |
|
|
20
|
+
| `llll` | Wed, Mar 26, 2026 3:27 PM | `ddd, D MMM YYYY h:mm A` |
|
|
21
|
+
| `L` | 03/26/2026 | `DD/MM/YYYY` |
|
|
22
|
+
| `LL` | March 26, 2026 | `D MMMM YYYY` |
|
|
23
|
+
| `LLL` | March 26, 2026 3:27 PM | `D MMMM YYYY h:mm A` |
|
|
24
|
+
| `LLLL` | Wednesday, March 26, 2026 3:27 PM | `dddd, D MMMM YYYY h:mm A` |
|
|
25
|
+
| `LT` | 3:27 PM | `h:mm A` |
|
|
26
|
+
| `LTS` | 3:27:45 PM | `h:mm:ss A` |
|
|
27
27
|
|
|
28
28
|
**Files to search:**
|
|
29
29
|
|
|
@@ -33,25 +33,21 @@ Grep: `date\(['"]([lL]{1,4}|LTS?)['"]\)`
|
|
|
33
33
|
**Example:**
|
|
34
34
|
|
|
35
35
|
Before:
|
|
36
|
-
|
|
37
36
|
```yaml
|
|
38
37
|
template: "{{ meeting_date | date('llll') }}"
|
|
39
38
|
```
|
|
40
39
|
|
|
41
40
|
After:
|
|
42
|
-
|
|
43
41
|
```yaml
|
|
44
42
|
template: "{{ meeting_date | date('ddd, D MMM YYYY h:mm A') }}"
|
|
45
43
|
```
|
|
46
44
|
|
|
47
45
|
Before:
|
|
48
|
-
|
|
49
46
|
```yaml
|
|
50
47
|
Last modified by {{ doc.updated.user.name | safe }} on {{ doc.updated.timestamp | date('lll')}}
|
|
51
48
|
```
|
|
52
49
|
|
|
53
50
|
After:
|
|
54
|
-
|
|
55
51
|
```yaml
|
|
56
52
|
Last modified by {{ doc.updated.user.name | safe }} on {{ doc.updated.timestamp | date('D MMM YYYY h:mm A')}}
|
|
57
53
|
```
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# Migration: Convert AgGrid cellRenderers to built-in `cell.type`
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
`@lowdefy/blocks-aggrid@5.1` introduces first-class column cell renderers that cover the patterns apps hand-build today with `_function` + `__nunjucks` + HTML strings:
|
|
6
|
+
|
|
7
|
+
| `cell.type` | Replaces |
|
|
8
|
+
| ----------- | ----------------------------------------------------------------- |
|
|
9
|
+
| `tag` | Coloured pill HTML via `color-mix`, antd Tag-style badges |
|
|
10
|
+
| `avatar` | Circle image/initials + name in a flex row |
|
|
11
|
+
| `link` | Hand-built `<a href="/pageId?…">` anchors |
|
|
12
|
+
| `date` | `__dayjs.format` / `__moment.format` inside `cellRenderer` |
|
|
13
|
+
| `boolean` | Conditional coloured "Yes/No" / "Verified/Unverified" text |
|
|
14
|
+
| `progress` | Conditional-colour percent pill (5-tier scale) |
|
|
15
|
+
| `number` | `valueFormatter` + `__string.concat` for currency/percent/compact |
|
|
16
|
+
|
|
17
|
+
Plus two column-level helpers:
|
|
18
|
+
|
|
19
|
+
- `ellipsis: N` — replaces `.ellipsis-N` class + manual `wrapText` / `autoHeight`.
|
|
20
|
+
- `cell.align: left | center | right` — replaces manual `cellStyle.justifyContent` + `headerClass`.
|
|
21
|
+
|
|
22
|
+
## Critical: this is a **report-only** codemod
|
|
23
|
+
|
|
24
|
+
**Do not auto-rewrite any column.** Every match below is a _candidate_ for conversion. Each candidate must be:
|
|
25
|
+
|
|
26
|
+
1. **Reviewed against its data shape.** The replacement reads from a specific field (or row-data path); the original cellRenderer may have computed, fallen-back, or reshaped data inline. If the aggregation/request doesn't already deliver the exact shape the built-in renderer expects, the column either needs a projection change in the request/aggregation, or the original `_function` cellRenderer should stay.
|
|
27
|
+
2. **Reviewed against its enum / colour lookup.** Many tag cells read colour from `__get: { key: __args: 0.data.x, from: __global: enums.y }` — this can migrate to `cell.colorFrom: x.color` **only if** the aggregation already hydrates `data.x.color`. If the colour lookup happens inside the cellRenderer template, the request has to start returning the resolved colour, or the tag's `colorMap` has to be baked into the column config.
|
|
28
|
+
3. **Confirmed with the column author.** Post each proposed change to the author of the table (or the nearest code owner) and get explicit sign-off before applying. Do not batch multiple columns into a single review.
|
|
29
|
+
|
|
30
|
+
The codemod runner should produce a **report with one entry per column matched**, including:
|
|
31
|
+
|
|
32
|
+
- File path + line number of the column defintion.
|
|
33
|
+
- The full existing cellRenderer snippet (for context).
|
|
34
|
+
- The proposed `cell.type` replacement.
|
|
35
|
+
- The **data contract** the replacement requires (fields read, shape expected).
|
|
36
|
+
- A checkbox / flag for the reviewer to approve or reject the individual column.
|
|
37
|
+
|
|
38
|
+
## Files to Search
|
|
39
|
+
|
|
40
|
+
Glob: `**/*.{yaml,yml,yaml.njk}`
|
|
41
|
+
|
|
42
|
+
Narrow to files containing AgGrid blocks first:
|
|
43
|
+
|
|
44
|
+
Grep: `type:\s*AgGrid(Alpine|Balham|Material|InputAlpine|InputBalham|InputMaterial)`
|
|
45
|
+
|
|
46
|
+
Then inside each matched file, look for `columnDefs:` entries.
|
|
47
|
+
|
|
48
|
+
## Detection Patterns
|
|
49
|
+
|
|
50
|
+
Each pattern below lists:
|
|
51
|
+
|
|
52
|
+
- A recognisable HTML / operator signature.
|
|
53
|
+
- The proposed `cell.type` replacement.
|
|
54
|
+
- The **questions the reviewer must answer** before approving.
|
|
55
|
+
|
|
56
|
+
### 1. Tag / badge pill
|
|
57
|
+
|
|
58
|
+
**Signature — the 12% / 30% `color-mix` convention:**
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
px-1.5 py-0.5 rounded text-\[10px\] font-semibold.*color-mix\(in srgb,\{\{\s*color\s*\}\}\s+12%
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Also matches variants:
|
|
65
|
+
|
|
66
|
+
- `color-mix(in srgb, {{ status.color }} 12%, transparent)`
|
|
67
|
+
- Inline `<span style="background:...;border:1px solid ...;color:...;">`
|
|
68
|
+
- Literal hex colours inside the style attribute.
|
|
69
|
+
|
|
70
|
+
**Proposed replacement:**
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
cell:
|
|
74
|
+
type: tag
|
|
75
|
+
colorFrom: <row.data path to colour> # when colour is on the row
|
|
76
|
+
# OR
|
|
77
|
+
colorMap: # when colour is per-enum-value
|
|
78
|
+
<value_1>: <colour>
|
|
79
|
+
<value_2>: <colour>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Reviewer must answer:**
|
|
83
|
+
|
|
84
|
+
- Where does the colour come from in the original — a global enum lookup, or a field on the row?
|
|
85
|
+
- Is the coloured value the cell's raw value, or a nested `title` / `label` field?
|
|
86
|
+
- Are there multiple fallback colours (e.g. `default` when enum lookup fails)? Map each to `colorMap.default` or the renderer's own default.
|
|
87
|
+
- Does the rendered text come straight from the cell value, or is it `value.title` / `data.xxx.label`? If not direct, the `field` on the column must point at the displayed text.
|
|
88
|
+
|
|
89
|
+
### 2. Avatar + name
|
|
90
|
+
|
|
91
|
+
**Signature:**
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
display:\s*inline-flex.*border-radius:\s*50%.*<img.*width="3[02]"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Also matches:
|
|
98
|
+
|
|
99
|
+
- Gray circle fallback with unicode 👤 or similar.
|
|
100
|
+
- `flex-shrink: 0`, `margin-right: 8px`.
|
|
101
|
+
- An `<a>` wrapping the image or name.
|
|
102
|
+
|
|
103
|
+
**Proposed replacement:**
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
cell:
|
|
107
|
+
type: avatar
|
|
108
|
+
nameField: <path> # row-data path for the visible name
|
|
109
|
+
srcField: <path> # row-data path for picture src (optional)
|
|
110
|
+
idField: <path> # seeds initials colour
|
|
111
|
+
link: # only if the original wrapped a link
|
|
112
|
+
pageId: <target>
|
|
113
|
+
urlQuery:
|
|
114
|
+
<key>: <row-data-path>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Reviewer must answer:**
|
|
118
|
+
|
|
119
|
+
- Where does the name string live? (`profile.name`, `assignee.full_name`, …)
|
|
120
|
+
- Where does the picture URL live, and is it nullable? (The built-in renders initials on null.)
|
|
121
|
+
- If wrapped in a link — what `pageId` and what `urlQuery` keys?
|
|
122
|
+
- Is there a distinct `id` for initials-colour seeding, or should it fall back to the name?
|
|
123
|
+
- If the initials fallback colour scheme in the original is brand-specific, it **doesn't** match the built-in scheme — skip conversion unless OK to change.
|
|
124
|
+
|
|
125
|
+
### 3. Link-styled cell
|
|
126
|
+
|
|
127
|
+
**Signature:**
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
cellRenderer:\s*\n\s*_function:\s*\n\s*__nunjucks:\s*\n.*<a href
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Also matches:
|
|
134
|
+
|
|
135
|
+
- `__string.concat: ['<a href="/', __args: 0.data._id, ...]`
|
|
136
|
+
- Links using `_module.pageId` inside the template.
|
|
137
|
+
|
|
138
|
+
**Proposed replacement:**
|
|
139
|
+
|
|
140
|
+
```yaml
|
|
141
|
+
cell:
|
|
142
|
+
type: link
|
|
143
|
+
pageId: <target>
|
|
144
|
+
urlQuery:
|
|
145
|
+
<key>: <row-data-path>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
And at the table level:
|
|
149
|
+
|
|
150
|
+
```yaml
|
|
151
|
+
events:
|
|
152
|
+
onCellLink:
|
|
153
|
+
- id: go
|
|
154
|
+
type: Link
|
|
155
|
+
params:
|
|
156
|
+
_event: link
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Reviewer must answer:**
|
|
160
|
+
|
|
161
|
+
- Does the link target a `pageId` or a full `href` (external URL)?
|
|
162
|
+
- What are the `urlQuery` keys and their row-data paths?
|
|
163
|
+
- Does the original open in a new tab (`target="_blank"`)? Use `newTab: true`.
|
|
164
|
+
- Is the link only shown conditionally (inside a `{% if %}`)? The built-in always renders; the conditional needs to move to the data layer (null value → null placeholder).
|
|
165
|
+
|
|
166
|
+
### 4. Date / timestamp
|
|
167
|
+
|
|
168
|
+
**Signature:**
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
cellRenderer:\s*\n\s*_function:\s*\n\s*__dayjs\.format:
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Or its moment equivalent `__moment.format`. Also matches `__string.concat` pipes that include `| date(...)` Nunjucks filter.
|
|
175
|
+
|
|
176
|
+
**Proposed replacement:**
|
|
177
|
+
|
|
178
|
+
```yaml
|
|
179
|
+
cell:
|
|
180
|
+
type: date
|
|
181
|
+
format: <format string> # defaults to 'YYYY-MM-DD HH:mm' — omit if using default
|
|
182
|
+
# relative: true # if the original used fromNow()
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Reviewer must answer:**
|
|
186
|
+
|
|
187
|
+
- Is the value an ISO string, a `Date`, or a millisecond number? The built-in calls `dayjs(value)` — anything dayjs can parse is fine.
|
|
188
|
+
- Is the format the default `YYYY-MM-DD HH:mm`? If so, drop the explicit `format` key.
|
|
189
|
+
- Is the original using `fromNow()` / `humanize()` / a custom relative output? Use `relative: true`.
|
|
190
|
+
- Timezone handling — the built-in uses local time; if the original forced UTC, preserve it or convert before passing.
|
|
191
|
+
|
|
192
|
+
### 5. Boolean / Yes-No
|
|
193
|
+
|
|
194
|
+
**Signature:**
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
cellRenderer:\s*\n\s*_function:\s*\n\s*__nunjucks:.*\{%\s*if\s+\w+\s*%\}.*<span.*color:\s*#(52c41a|[0-9a-f]+).*Yes.*<span.*No
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Also matches patterns using `app_config.colors.success` / `var(--ant-color-success)`.
|
|
201
|
+
|
|
202
|
+
**Proposed replacement:**
|
|
203
|
+
|
|
204
|
+
```yaml
|
|
205
|
+
cell:
|
|
206
|
+
type: boolean
|
|
207
|
+
# trueLabel / falseLabel / trueColor / falseColor — override only if the original deviates
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Reviewer must answer:**
|
|
211
|
+
|
|
212
|
+
- Are the labels literally "Yes" / "No", or something domain-specific (`Verified`/`Unverified`, `Active`/`Inactive`)?
|
|
213
|
+
- What are the colours? The built-in defaults to `--ant-color-success` / `--ant-color-text-quaternary`. If the original uses brand-specific tokens, set `trueColor` / `falseColor`.
|
|
214
|
+
- Does the original render `—` or something else for null? The built-in renders em-dash `—` — confirm that matches.
|
|
215
|
+
- Does the rendered text include an icon? (✓ ✗ 👍 etc.) — **skip conversion** unless the column owner approves text-only.
|
|
216
|
+
|
|
217
|
+
### 6. Progress / percentage
|
|
218
|
+
|
|
219
|
+
**Signature — 5-tier coloured pill:**
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
\{%\s*if\s+\w+\s*<\s*20.*#dc2f02.*%\}.*\{%\s*elseif.*<\s*40.*#e85d04.*%\}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Also matches apps using `app_config.colors.progress_low` etc.
|
|
226
|
+
|
|
227
|
+
**Proposed replacement:**
|
|
228
|
+
|
|
229
|
+
```yaml
|
|
230
|
+
cell:
|
|
231
|
+
type: progress
|
|
232
|
+
# thresholds: [20, 40, 60, 80] # defaults
|
|
233
|
+
# colors: [<five colour tokens>] # defaults to antd error → success scale
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Reviewer must answer:**
|
|
237
|
+
|
|
238
|
+
- Are the thresholds exactly `[20, 40, 60, 80]`? If not, set `thresholds`.
|
|
239
|
+
- Are the colours the same antd error → warning → gold → info → success? If the original used brand colours, set `colors` array (length = thresholds.length + 1).
|
|
240
|
+
- Is the value already a 0–100 number, or a 0–1 fraction? If fraction, either multiply in the aggregation or use `cell.type: number` with `format: percent` instead.
|
|
241
|
+
- Is there a null / unstarted label other than "None"? Use `nullLabel`.
|
|
242
|
+
|
|
243
|
+
### 7. Number formatting
|
|
244
|
+
|
|
245
|
+
**Signature:**
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
valueFormatter:\s*\n\s*_function:\s*\n.*__string\.concat:
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Or manual `Intl.NumberFormat` / `toLocaleString` usage, or hand-built percent with `%` suffix.
|
|
252
|
+
|
|
253
|
+
**Proposed replacement — pick by shape:**
|
|
254
|
+
|
|
255
|
+
```yaml
|
|
256
|
+
# Currency
|
|
257
|
+
cell:
|
|
258
|
+
type: number
|
|
259
|
+
format: currency
|
|
260
|
+
currency: USD
|
|
261
|
+
decimals: 0
|
|
262
|
+
|
|
263
|
+
# Accounting (negatives in parens, green/red)
|
|
264
|
+
cell:
|
|
265
|
+
type: number
|
|
266
|
+
format: currency
|
|
267
|
+
currency: USD
|
|
268
|
+
decimals: 2
|
|
269
|
+
negative: parentheses
|
|
270
|
+
signColor: true
|
|
271
|
+
|
|
272
|
+
# Percent
|
|
273
|
+
cell:
|
|
274
|
+
type: number
|
|
275
|
+
format: percent
|
|
276
|
+
decimals: 1
|
|
277
|
+
|
|
278
|
+
# Compact (K/M/B)
|
|
279
|
+
cell:
|
|
280
|
+
type: number
|
|
281
|
+
format: compact
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Reviewer must answer:**
|
|
285
|
+
|
|
286
|
+
- Is the raw value the final number, or a string (e.g. `'12,345.67'`)? `cell.type: number` expects a number — coerce in the aggregation if needed.
|
|
287
|
+
- For percent: is the field a fraction (0.184) or a 0-100 integer (18.4)? `Intl.NumberFormat` with `style: 'percent'` **multiplies by 100** — adjust the field or use `format: number` + `suffix: '%'`.
|
|
288
|
+
- For currency: which currency, locale, and decimal precision? Make sure the currency code is ISO 4217.
|
|
289
|
+
- Does the original right-align? The built-in auto-right-aligns numbers — confirm this matches the column's visual intent. Override via `align: left` if needed.
|
|
290
|
+
- Does the original colour negatives red / positives green? Set `signColor: true`.
|
|
291
|
+
|
|
292
|
+
### 8. Ellipsis / truncated text
|
|
293
|
+
|
|
294
|
+
**Signature:**
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
cellRenderer:\s*\n\s*_function:\s*\n\s*__string\.concat:\s*\n.*ellipsis-\d
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Also matches bare `class="ellipsis-4"` / `class="ellipsis-2"` patterns inside any cellRenderer.
|
|
301
|
+
|
|
302
|
+
**Proposed replacement:**
|
|
303
|
+
|
|
304
|
+
```yaml
|
|
305
|
+
ellipsis: <N> # column-level; omit cellRenderer entirely
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
(Automatically sets `wrapText: true` + `autoHeight: true` + applies the line-clamp.)
|
|
309
|
+
|
|
310
|
+
**Reviewer must answer:**
|
|
311
|
+
|
|
312
|
+
- Is the content plain text, or HTML? The built-in ellipsis renderer treats the value as text — if the original included formatting, this path doesn't fit. Keep the custom renderer.
|
|
313
|
+
- Does the original wrap a **tag** or **link** inside the clamp? `ellipsis: N` only wraps plain values — for rich-cell clamping, skip this conversion.
|
|
314
|
+
- Line count: 1, 2, 3, 4, 5, or 6? Only those counts are supported.
|
|
315
|
+
|
|
316
|
+
### 9. Header & cell alignment
|
|
317
|
+
|
|
318
|
+
**Signature:**
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
headerClass:\s*ag-right-aligned-header
|
|
322
|
+
cellStyle:\s*\n.*justifyContent:\s*flex-end
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Proposed replacement:**
|
|
326
|
+
|
|
327
|
+
```yaml
|
|
328
|
+
cell:
|
|
329
|
+
type: <existing type or number>
|
|
330
|
+
align: right
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Remove the explicit `headerClass` and `cellStyle.justifyContent`.
|
|
334
|
+
|
|
335
|
+
**Reviewer must answer:**
|
|
336
|
+
|
|
337
|
+
- Did the original set both cell and header alignment consistently? If only one side was aligned, the unified `align:` key changes visible behaviour — confirm.
|
|
338
|
+
|
|
339
|
+
## Column-level guardrails the codemod MUST emit alongside every proposal
|
|
340
|
+
|
|
341
|
+
For every column flagged as a conversion candidate, the report entry must include:
|
|
342
|
+
|
|
343
|
+
1. **"Field contract" block** — list every row-data path the built-in renderer will read, with a note whether the current aggregation/request delivers that shape. If unknown, mark as "needs aggregation inspection".
|
|
344
|
+
2. **"Visual parity" warning** — remind the reviewer to compare a few rows before/after to confirm padding, colours, row height, and null handling are acceptable.
|
|
345
|
+
3. **"Dark-mode check"** — new renderers use antd tokens; if the old hand-built HTML used hard-coded hex, dark mode may look different (usually better, occasionally surprising).
|
|
346
|
+
4. **"Sort / filter / CSV export"** — `cellRenderer` does not affect `valueGetter` / `valueFormatter` used by sort / filter / export. The built-in renderers _display_ only. If the original used a `cellRenderer` that also transformed the value, check that sort/filter/export still behave correctly.
|
|
347
|
+
5. **"Events"** — if the column participated in `onCellClick` with behaviour branching on column, migrating to `cell.type: link` + `onCellLink` is usually cleaner but changes the event shape. Audit event handlers.
|
|
348
|
+
|
|
349
|
+
## Apply workflow (per column)
|
|
350
|
+
|
|
351
|
+
1. Run the codemod in report mode. Output goes to a reviewable file (Markdown or JSON), one section per matched column.
|
|
352
|
+
2. For each section, the reviewer answers the questions above and either:
|
|
353
|
+
- **Approves** → manually apply the proposed `cell:` block in the column def.
|
|
354
|
+
- **Defers** → leave the column's custom cellRenderer untouched.
|
|
355
|
+
- **Requires data change** → log a ticket to adjust the aggregation / request to hydrate the field shape the built-in needs, then revisit.
|
|
356
|
+
3. After conversion, remove the now-unused `_function` / `__nunjucks` / `__dayjs.format` blocks and any `valueFormatter` that only formatted for display.
|
|
357
|
+
4. Test the page in the browser — confirm visual parity and that click/sort/filter/export behave identically.
|
|
358
|
+
5. Commit per-column (small, reviewable diffs) so a regression can be isolated quickly.
|
|
359
|
+
|
|
360
|
+
## What to NOT convert
|
|
361
|
+
|
|
362
|
+
- **Cells with conditional visual branching** (showing a completely different shape when `status == 'error'` vs default). The built-ins are single-shape.
|
|
363
|
+
- **Cells that compose multiple widgets** (avatar + status dot + text + link + icon). Split into separate columns, or keep the custom renderer.
|
|
364
|
+
- **Cells driven by a request that isn't yet aggregated** (e.g. the colour / icon is computed in Nunjucks from a `_global` that's not on the row). Either pre-compute in the aggregation or keep.
|
|
365
|
+
- **Cells where the `cellRenderer` also mutates `params` for ag-grid's internal sort/filter**. The built-ins only render — they don't touch value semantics.
|
|
366
|
+
- **Cells inside an `AgGridInput*`** where the cell is editable. The built-ins render display-only output; for editable cells, keep your custom `cellRenderer` / `cellEditor`.
|
|
367
|
+
|
|
368
|
+
## Verification
|
|
369
|
+
|
|
370
|
+
For each converted column:
|
|
371
|
+
|
|
372
|
+
```
|
|
373
|
+
pnpm --filter=@lowdefy/blocks-aggrid build
|
|
374
|
+
pnpm ldf:b
|
|
375
|
+
pnpm ldf:d
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Navigate to the page, verify the column visually matches the prior output, click-test, sort-test, filter-test, export-test. Capture a before/after screenshot for the PR.
|
|
379
|
+
|
|
380
|
+
## Non-goals of this codemod
|
|
381
|
+
|
|
382
|
+
- No auto-apply. Ever. Even the "safe" conversions (date, ellipsis) require author confirmation because the data contract may differ.
|
|
383
|
+
- No bulk commit. One PR = one column, one reviewer sign-off.
|
|
384
|
+
- No cross-file refactors. If multiple tables share a module that currently returns HTML, migrate the module call-sites first, then the module itself in a follow-up.
|