fix(personalization-plugin): address review feedback from #971 - #1208
Conversation
🦋 Changeset detectedLatest commit: 1dc8603 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
There was a problem hiding this comment.
Pull request overview
Follow-up fixes for @sanity/personalization-plugin after the monorepo migration, addressing correctness issues (pagination, preview rendering, render-phase side effects), clarifying plugin identity strings, and updating integration documentation/examples.
Changes:
- Fix LaunchDarkly experiment pagination by advancing
offsetbetween pages. - Correct plugin
namestrings, fix preview title operator precedence, make reference preview traversal resilient, and move “active” initialization to an effect. - Update docs/examples (LaunchDarkly/GrowthBook) and add a patch changeset.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| plugins/@sanity/personalization-plugin/src/launchDarkly/utils.ts | Fixes pagination by incrementing offset per page. |
| plugins/@sanity/personalization-plugin/src/launchDarkly/index.ts | Corrects plugin name to LaunchDarkly-specific identity. |
| plugins/@sanity/personalization-plugin/src/growthbook/index.ts | Fixes misspelled plugin name. |
| plugins/@sanity/personalization-plugin/src/fieldExperiments.tsx | Fixes preview title precedence and misspelled plugin name. |
| plugins/@sanity/personalization-plugin/src/components/VariantPreview.tsx | Makes preview path traversal safe with optional chaining. |
| plugins/@sanity/personalization-plugin/src/components/Select.tsx | Removes unused props/imports from Select input. |
| plugins/@sanity/personalization-plugin/src/components/ExperimentItem.tsx | Moves activation patch to useEffect to avoid render-phase side effects. |
| plugins/@sanity/personalization-plugin/README.md | Removes stale standalone-repo sections and TOC entries. |
| plugins/@sanity/personalization-plugin/launchdarkly.md | Updates Studio version wording + usage example. |
| plugins/@sanity/personalization-plugin/growthbook.md | Updates Studio version wording + config example key. |
| .changeset/personalization-plugin-review-fixes.md | Adds a patch changeset describing the fixes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The offset was a constant, so pagination re-fetched the first page and could loop forever when a page returned exactly `limit` items. Advance the offset by `limit` between pages and stop once a short page is returned.
Fix the misspelled `personalistaion` plugin names and stop the LaunchDarkly entry point from identifying itself as the GrowthBook plugin, which also avoids a name collision between the two entry points.
`||` bound tighter than the ternary, so a truthy `base.title` made the condition select the whole `base` object as the title. Parenthesize the string fallback so the title stays a string.
Walk the reference preview `select` path with optional chaining so a missing intermediate field yields `undefined` instead of throwing and breaking the Studio preview.
Calling `onChange` during render is a render-phase side effect that can trigger repeated patches. Move the one-time `active` activation into a `useEffect` guarded on the falsy `active` value.
Remove the never-used `aditionalChangePath`/`clearSubValueOnChange` props (and the now-unused `Path` import) from the `Select` component.
…tions - Use the real `fieldLevelExperiments` export in the LaunchDarkly usage example - Use the `project` config key (not `projectId`) in the GrowthBook example - Make the supported Studio version version-agnostic instead of claiming v3 - Remove the stale standalone-repo `Develop & test` / `Release new version` sections and their dead table-of-contents links
Address PR review feedback: - Retitle the LaunchDarkly doc and its table-of-contents link from the old `launchDarklyFieldLevel` to `LaunchDarkly`, matching the GrowthBook doc and the actual `fieldLevelExperiments` export - Fix the `Previously know` -> `Previously known` typo in both docs - Fix the duplicated slash in the LaunchDarkly link and lowercase the self-anchors so they resolve
21acace to
4c0673b
Compare
✅ E2E Tests🟢 24 passed • view full report • view run Studio: https://plugins-e2e-test-studio-opi2uigos.sanity.dev Datasets: |
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
…el-personalization-620c Resolve conflicts with #1208 review fixes: keep ArrayItem rename while moving activation into useEffect, keep explicit preview title typing, and use optional-chaining path traversal. Also fix the personalization plugin name spelling to match main.
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @sanity/sanity-plugin-async-list@3.0.0 ### Major Changes - [#1087](#1087) [`f4e7fcc`](f4e7fcc) Thanks [@stipsan](https://github.com/stipsan)! - Refactor the input into a real React component and fix the `async-list-undefined` namespace/id - **Breaking:** `AsyncList` is now a regular React component that takes a single `props` argument (the standard Sanity input props plus an `options` field), instead of being called as `AsyncList(props, options)`. For the `components.input` slot, use the new `createAsyncListInput(options)` factory: `input: createAsyncListInput({loader})`. This makes the input safe under the Rules of Hooks and lets the React Compiler optimize it. - **Breaking:** the secrets namespace and DOM `id` are no longer derived as `async-list-${schemaType}` (which became the literal `async-list-undefined` for component usage). The DOM `id` now uses Sanity's stable per-field id, and the secrets namespace falls back to `async-list` (instead of `async-list-undefined`) when no `schemaType`/`secrets.namespace` is available. When using the component with `secrets`, set an explicit `secrets.namespace`; a dev warning is logged if it is missing. - Fix: the debounced search handler is stable and is cancelled on unmount, so it no longer drops queued calls or updates state on an unmounted tree. - Fix: the value-change handler no longer depends on the whole `props` object, avoiding unnecessary `Autocomplete` re-renders. - Fix: loader results are validated to ensure each option's `value` is a string. ### Patch Changes - Updated dependencies [[`c61bb44`](c61bb44)]: - @sanity/studio-secrets@4.0.15 ## @sanity/personalization-plugin@3.1.0 ### Minor Changes - [#1226](#1226) [`1143bd6`](1143bd6) Thanks [@jjburbridge](https://github.com/jjburbridge)! - Add `fieldLevelPersonalization` export for segment-based field personalization. Segments can be defined statically, fetched from an external API, or stored in your Sanity dataset. `FieldPluginConfig` is renamed to `ExperimentFieldPluginConfig` (a deprecated type alias is kept for compatibility). ### Patch Changes - [#1208](#1208) [`51317b2`](51317b2) Thanks [@stipsan](https://github.com/stipsan)! - Address review feedback from the migration into the monorepo: - Fix LaunchDarkly experiment pagination so `offset` advances between pages, preventing duplicate results and a possible infinite loop - Correct misspelled plugin names (`personalistaion` → `personalization`) and stop the LaunchDarkly entry point from identifying itself as the GrowthBook plugin - Fix an operator-precedence bug in the experiment field preview that could set the title to the entire field object - Walk reference preview paths with optional chaining to avoid throwing on missing intermediate fields - Move the experiment item activation patch out of render and into an effect - Remove unused `Select` props - Fix the GrowthBook (`project` config key) and LaunchDarkly (`fieldLevelExperiments` usage) docs and drop the stale standalone-repo Studio version / tooling references - Updated dependencies [[`c61bb44`](c61bb44)]: - @sanity/studio-secrets@4.0.15 ## sanity-plugin-cloudinary@2.1.0 ### Minor Changes - [#1233](#1233) [`36f3fe2`](36f3fe2) Thanks [@ChrisLaRocque](https://github.com/ChrisLaRocque)! - Add performant preview images Asset previews and diffs now build an optimized, scaled-down preview URL with `@cloudinary/url-gen` (a 400px-wide transformation) when a cloud name is configured, instead of loading the full-size original. This keeps the Studio fast when previewing large Cloudinary assets, and falls back to the stored asset URL when no cloud name is available. - [#1227](#1227) [`3b02635`](3b02635) Thanks [@pgurley](https://github.com/pgurley), [@atlvis](https://github.com/atlvis)! - Add `cloudinaryReferencePlugin` for storing Cloudinary assets as reusable document references - New `cloudinaryReferencePlugin` registers the schema types needed to reference Cloudinary assets as documents - New `cloudinaryAssetDocument` type stores a Cloudinary asset as a standalone document - New `cloudinaryAssetReference` type references those asset documents, with a custom input for selecting and managing assets through the Cloudinary Media Library - `openMediaSelector` now supports a `showHandler` callback and a `folder` option, so the select button can show a loading state and scope the library to a folder - Fixed the internal name of `cloudinaryAssetSourcePlugin` (`cloudinart-asset-source` → `cloudinary-asset-source`) ### Patch Changes - [#1236](#1236) [`7ec89b6`](7ec89b6) Thanks [@stipsan](https://github.com/stipsan)! - - Fix the `cloudinaryAssetSourcePlugin` name, which was mistakenly registered as `cloudinart-asset-source` - Wait for the Cloudinary Media Library script to finish loading before opening it, avoiding runtime errors when several inputs mount at once - Fix a user-facing typo in the asset source loading message ("Media Libary" → "Media Library") - Remove an invalid `src`-less `<track>` element from the video preview - Correct the README usage examples (`defineConfg` → `defineConfig`) and drop stale standalone-repo "Develop & test" / "Release new version" instructions - Updated dependencies [[`c61bb44`](c61bb44)]: - @sanity/studio-secrets@4.0.15 ## sanity-plugin-media@6.1.0 ### Minor Changes - [#1119](#1119) [`ab39674`](ab39674) Thanks [@lud-hu](https://github.com/lud-hu)! - Add an "Edit Media" asset source so media details (alt text, title, tags, etc.) can be edited directly from an image or file field on a document, without opening the full Media tool. - [#1117](#1117) [`6f4316d`](6f4316d) Thanks [@nickeforsberg](https://github.com/nickeforsberg)! - Add an `excludeTags` option that hides assets referencing the listed `media.tag` slugs (`name.current` values). Excluded assets are omitted from the Media browser grid and asset-picker queries, and the matching tags are hidden from the tag sidebar and tag search facet. The asset edit dialog still lists all tags so you can assign or remove them on an open asset. - [#1121](#1121) [`30fa7e2`](30fa7e2) Thanks [@bobbygeo](https://github.com/bobbygeo), [@pedrobonamin](https://github.com/pedrobonamin)! - Add folder management to the media browser - Organise assets into nested folders, backed by a new `media.folder` document type (a `name` plus a weak `parent` reference) and a single weak `opt.media.folder` reference on each asset (mirroring how tags work). Renaming a folder is a one-field document write regardless of how many assets it contains. - Browse folders in a dedicated sidebar tree, with breadcrumb navigation, create / rename / delete flows, a bulk "Move to folder" dialog, and per-asset folder controls in the asset details dialog. The default "All assets" view lists every asset; opening a folder filters the list to that folder. - Deleting a folder removes only the folder document — its assets stay in the library with their folder assignment cleared, and nested folders move up one level. - Support multi-select insert when the media plugin is used as an asset source for array (multiple) fields. - [#1115](#1115) [`e7ec6e6`](e7ec6e6) Thanks [@am0wa](https://github.com/am0wa)! - Extract `image` (`sanity.imageExifTags`) metadata on asset upload and include it in asset queries. The asset edit dialog now pre-fills the Description field from the image's `ImageDescription` EXIF tag as a fallback for image assets. - [#1118](#1118) [`186e29c`](186e29c) Thanks [@Polleke007](https://github.com/Polleke007), [@joepvandepol](https://github.com/joepvandepol)! - Add the ability to replace a selected asset with another one and update all of its references across documents When a single asset is selected, a new **Replace** action opens an overview where you can pick a replacement asset. Every document that references the original asset (including deeply nested image fields) is re-pointed to the chosen asset. Ported from sanity-io/sanity-plugin-media#236. ### Patch Changes - [#1108](#1108) [`1dc3a7b`](1dc3a7b) Thanks [@oxygensmith](https://github.com/oxygensmith)! - Add `crossOrigin="anonymous"` to the image thumbnail component so thumbnails load in Firefox. Firefox's Opaque Response Blocking would otherwise block Sanity CDN asset responses (which send `Vary: Origin`) when requested without an `Origin` header. ## @sanity/plugin-kit@10.0.3 ### Patch Changes - [#1698](#1698) [`d3dedd3`](d3dedd3) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency oxlint to ^1.76.0 - [#1779](#1779) [`5555795`](5555795) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency oxfmt to ^0.61.0 - [#1799](#1799) [`93858fc`](93858fc) Thanks [@stipsan](https://github.com/stipsan)! - Prefer function components via `react/prefer-function-component` (with `allowErrorBoundary`) instead of banning `Component`/`PureComponent` imports ## @sanity/cross-dataset-duplicator@2.0.13 ### Patch Changes - [#1228](#1228) [`71d77d5`](71d77d5) Thanks [@rohanvachheta](https://github.com/rohanvachheta)! - Handle reference errors during duplication by fetching missing referenced documents (including transitive refs, respecting `filter`), re-uploading any recovered assets, and retrying the transaction, with a one-by-one commit fallback - Updated dependencies [[`c61bb44`](c61bb44)]: - @sanity/studio-secrets@4.0.15 ## @sanity/embeddings-index-ui@4.0.13 ### Patch Changes - [#1792](#1792) [`c61bb44`](c61bb44) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency react-rx to ^4.2.5 ## @sanity/form-toolkit@3.0.13 ### Patch Changes - [#1234](#1234) [`1dd36c8`](1dd36c8) Thanks [@stipsan](https://github.com/stipsan)! - Address post-migration review feedback: - `FormRenderer` now renders interactive (uncontrolled) inputs when no `getFieldState` is provided, so the documented native HTML form usage works out of the box - Uncontrolled text and textarea fields honor `options.defaultValue` (controlled fields keep form-library state only, so UI and submit values stay in sync) - `FormRenderer` falls back to `field.name` for the React key when a field has no `_key` - The shared HubSpot/Mailchimp request handler defaults to the Next.js-compatible handler instead of throwing when no framework environment variable is detected, and short-circuits CORS preflight (`OPTIONS`) requests - Corrected the `formSchema`, `formiumInput`, and `mailchimpInput` usage examples (import paths and required options), README typos, and example/dev-workflow references - Updated dependencies [[`f4e7fcc`](f4e7fcc)]: - @sanity/sanity-plugin-async-list@3.0.0 ## @sanity/language-filter@5.0.14 ### Patch Changes - [#1792](#1792) [`c61bb44`](c61bb44) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency react-rx to ^4.2.5 ## @sanity/studio-secrets@4.0.15 ### Patch Changes - [#1792](#1792) [`c61bb44`](c61bb44) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency react-rx to ^4.2.5 ## sanity-plugin-hotspot-array@5.0.12 ### Patch Changes - [#1789](#1789) [`9628df4`](9628df4) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency motion to ^12.43.0 ## sanity-plugin-iframe-pane@5.0.30 ### Patch Changes - [#1789](#1789) [`9628df4`](9628df4) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency motion to ^12.43.0 ## sanity-plugin-internationalized-array@5.1.25 ### Patch Changes - [#1765](#1765) [`97937c4`](97937c4) Thanks [@pedrobonamin](https://github.com/pedrobonamin)! - Add a stable `data-testid` on the document-level add-translations panel for e2e coverage ## sanity-plugin-mux-input@5.0.8 ### Patch Changes - [#1792](#1792) [`c61bb44`](c61bb44) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency react-rx to ^4.2.5 ## sanity-plugin-studio-smartling@5.0.14 ### Patch Changes - [#1216](#1216) [`d4dd45b`](d4dd45b) Thanks [@stipsan](https://github.com/stipsan)! - Harden the Smartling adapter and fix docs: - Send the secret credentials JSON verbatim during authentication instead of double-encoding it, which could break auth with proxies that forward the request body as-is - Throw a clear error (surfacing Smartling's message) when authentication does not return an access token, instead of a cryptic `TypeError` - Avoid throwing when no existing job is found or when a translation/progress response is missing its expected payload - Guard the progress calculation against a zero total word count so empty documents no longer report `NaN`/`Infinity` - Fix the `additionalDeserializers` option name in the advanced configuration docs (was `additonalDeserializers`) and update the README development/release instructions for the monorepo ## sanity-plugin-workflow@3.0.39 ### Patch Changes - [#1789](#1789) [`9628df4`](9628df4) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency motion to ^12.43.0 ## @sanity/document-internationalization@6.2.28 ## @sanity/sfcc@1.0.25 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > <sup>[Cursor Bugbot](https://cursor.com/bugbot) is generating a summary for commit 9246b02. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Follow-up to the
@sanity/personalization-pluginmigration (#971), addressing the Copilot review.Fixes
Source
src/launchDarkly/utils.ts):offsetwas a constant, so each loop re-fetched the first page and could loop forever when a page returned exactlylimititems. The offset now advances bylimitbetween pages and stops on a short page.src/fieldExperiments.tsx,src/launchDarkly/index.ts,src/growthbook/index.ts): corrected the misspelledpersonalistaion→personalization, and the LaunchDarkly entry point no longer identifies itself as the GrowthBook plugin (which also removes a name collision between the two entry points).src/fieldExperiments.tsx):||bound tighter than the ternary, so a truthybase.titleselected the entirebaseobject as the title. The string fallback is now parenthesized.src/components/VariantPreview.tsx): theselectpath is now walked with optional chaining so a missing intermediate field yieldsundefinedinstead of throwing.src/components/ExperimentItem.tsx): theactiveactivation patch moved out of render and into auseEffectguarded on the falsy value, avoiding a render-phase side effect.src/components/Select.tsx): removed the never-usedaditionalChangePath/clearSubValueOnChangeprops and the now-unusedPathimport.Docs
launchdarkly.md: usage example now calls the actualfieldLevelExperimentsexport.growthbook.md: config example now uses theprojectkey (not the ignoredprojectId).launchdarkly.md/growthbook.md: supported Studio version is now version-agnostic instead of claiming "v3".README.md+ both integration docs: removed the stale standalone-repoDevelop & test/Release new versionsections and their dead table-of-contents links.A
patchchangeset is included for@sanity/personalization-plugin.Walkthrough
End-to-end demo of the field-level experiment flow in the test studio (kitchen-sink workspace) after the fixes — default value, beaker "Add experiment" action, the
Selectexperiment dropdown, variant entry, and readable preview text (no[object Object]):personalization_experiment_flow_demo.mp4
Completed experiment field with selected experiment and a variant showing readable preview text
The non-UI logic fixes (preview-title precedence and LaunchDarkly pagination) are demonstrated with a before/after script — the precedence fix returns a string instead of the whole object, and pagination now fetches all flags exactly once instead of looping on page 0:
logic_fix_demo.log
Testing
pnpm format— no changespnpm lint— passes (--deny-warnings)pnpm build— 49/49 packages buildpnpm test run— 885 tests passTo show artifacts inline, enable in settings.