Renames the deprecated onChange event handler to onCheckedChange on the
Pluma form-control components that standardized on onCheckedChange in v3:
| Before | After |
|---|---|
<Checkbox onChange={…} /> | <Checkbox onCheckedChange={…} /> |
<Radio onChange={…} /> | <Radio onCheckedChange={…} /> |
<OptionCard onChange={…} /> | <OptionCard onCheckedChange={…} /> |
Affected components: Checkbox, Radio, OptionCard (React) and
PlumaCheckbox, PlumaRadio, PlumaOptionCard (Ember). The group components
RadioGroup / CheckboxGroup / OptionCardGroup keep their own onChange
prop and are not touched.
There are two transforms — one for React (.tsx/.ts/.jsx/.js) and one
for Ember (.gts/.gjs/.hbs). Run whichever apply to your app.
Run it through the Pluma CLI from inside your consuming app. Always start with a dry run to review the diff before applying:
Both frameworks accept --dry / -d. The React path also forwards standard
jscodeshift flags, so -p prints transformed output.
React. Only JSX elements whose tag resolves (via the import in the same
file) to Checkbox, Radio, or OptionCard from
@customerio/pluma-components/react (or the bare @customerio/pluma-components
entry). Aliased imports (import { Checkbox as C } from …) are handled. A
local component named Checkbox that wasn't imported from Pluma is left alone,
as is onChange on any native <input> or on another component.
Ember. Only @onChange on the <PlumaCheckbox …>, <PlumaRadio …>, and
<PlumaOptionCard …> element nodes is renamed. @onChange on any other tag —
including <PlumaRadioGroup> / <PlumaCheckboxGroup> — is left alone.
After running, you can grep for any leftover onChange on these components to
catch anything reached through indirection the codemod can't see.
For the narrow case where it can prove the rewrite is safe, the codemod also migrates the callback to the new signature:
It auto-fixes only when the callback has exactly one parameter whose every use
is <param>.target.checked / <param>.currentTarget.checked — nothing else
(no e.preventDefault(), no e.target.value, not passed along). The param
becomes a checked boolean (or isChecked, if checked is already in scope,
to avoid a no-shadow). If both names are taken, or the body contains a nested
function, it stays rename-only.
In React this applies to two shapes:
onChange={(e) => …} / onChange={function (e) { … }}.onChange={myFn} where myFn is a
const/let/function/useCallback binding defined in the same file.
The definition is rewritten once.In Ember it applies to strict-mode .gts/.gjs backing-class methods —
see Ember below. Classic .hbs stays rename-only.
For strict-mode .gts/.gjs (template co-located with the component
class), the codemod also rewrites the backing class method:
Same safety rule as React: this.handleToggle's signature is rewritten only
when every reference to it is a migrated @onChange site on
PlumaCheckbox/PlumaRadio/PlumaOptionCard. If it's also wired to
{{on "change" this.handleToggle}}, passed via {{fn this.handleToggle}},
called elsewhere in the class, or used by any other component, the method is
left alone and the site is reported for a manual fix. The body must match the
same narrow pattern (single event param used only as .target.checked /
.currentTarget.checked). Both @action/method and class-field-arrow shapes
are handled. Under the hood the .gts/.gjs file is split into JS + template
regions with content-tag;
if anything about the round-trip looks off, the transform falls back to
rename-only rather than risk touching the file.
Classic .hbs (template + a separate .js/.ts class file) stays
rename-only + report: the backing class is in another file the codemod can't
see, so update those methods by hand.
After a run the codemod prints a per-site checklist, split into two groups so you know exactly what still needs attention. It prints in both dry and write runs:
.hbs Ember sites). Update these by hand to read the new checked argument.Callbacks that provably ignore their argument (0 params, or a single unused param) are renamed silently — they need no fix and aren't listed.
The React/Ember transform engines and reporting are shared across the
attribute-rename codemods under ../../_shared/; this codemod is a thin config
wrapper (attrRenames for the rename, attrRenameNotes for the review text,
and the opt-in rewriteCheckedCallback for the React callback auto-fix — the
other codemods don't set it, so they're unaffected). There's a test suite under
tests/:
It uses Node's built-in test runner (node --test).