Repository navigation
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
🦋 Changeset detectedLatest commit: 8ff619a 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 |
✅ E2E Tests🟢 46 passed • view full report • view run Studio: https://plugins-e2e-test-studio-igr6okr2o.sanity.dev Datasets: |
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Reduced-motion iframe fade still plays
- Confirmed that Motion's variant transition replaces rather than merges with the MotionConfig one, and made the
activevariant dynamic viacustom={prefersReducedMotion}so reduced-motion users fall back to the config's zero-duration transition instead of the 400ms hold and fade.
- Confirmed that Motion's variant transition replaces rather than merges with the MotionConfig one, and made the
Or push these changes by commenting:
@cursor push 87e647f5f2
Preview (87e647f5f2)
diff --git a/plugins/sanity-plugin-iframe-pane/src/Iframe.tsx b/plugins/sanity-plugin-iframe-pane/src/Iframe.tsx
--- a/plugins/sanity-plugin-iframe-pane/src/Iframe.tsx
+++ b/plugins/sanity-plugin-iframe-pane/src/Iframe.tsx
@@ -322,6 +322,8 @@
ref: iframe,
} = props
+ const prefersReducedMotion = usePrefersReducedMotion()
+
function handleIframeLoad() {
setLoading(false)
setReloading(false)
@@ -362,6 +364,7 @@
// (its first state is `background`, the same as the old `initial`).
initial={false}
variants={iframeVariants}
+ custom={prefersReducedMotion}
animate={[
loading ? 'background' : 'active',
reloading ? 'reloading' : 'idle',
@@ -401,13 +404,15 @@
reloading: {
scale: [1, 1, 1, 0.98],
},
- active: {
+ active: (prefersReducedMotion: boolean) => ({
opacity: 1,
scale: 1,
// Hold while the spinner fades out, then fade in: the same choreography as the
- // `[0, 0, 1]` keyframes this replaces, which Motion would replay from 0 on every reveal
- transition: {opacity: {delay: 0.4, duration: 0.4}},
- },
+ // `[0, 0, 1]` keyframes this replaces, which Motion would replay from 0 on every reveal.
+ // A variant transition replaces the `MotionConfig` one instead of merging with it, so
+ // reduced motion has to fall back to it explicitly.
+ transition: prefersReducedMotion ? undefined : {opacity: {delay: 0.4, duration: 0.4}},
+ }),
}
function Loading({iframeSize}: {iframeSize: IframeSizeKey}) {You can send follow-ups to the cloud agent here.
5b897da to
c9f7dbd
Compare
c9f7dbd to
154271f
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix could not produce a summary for this run.
Or push these changes by commenting:
@cursor push 8b5da8b80f
Preview (8b5da8b80f)
diff --git a/plugins/sanity-plugin-iframe-pane/src/Iframe.tsx b/plugins/sanity-plugin-iframe-pane/src/Iframe.tsx
--- a/plugins/sanity-plugin-iframe-pane/src/Iframe.tsx
+++ b/plugins/sanity-plugin-iframe-pane/src/Iframe.tsx
@@ -366,8 +366,14 @@
// Hold while the spinner fades out, then fade in: the choreography of the `[0, 0, 1]`
// opacity keyframes `active` used to have, which a replay would run from 0 again. An
// explicit transition replaces `MotionConfig` for every value, so reduced motion is
- // honoured here, for all of them.
- transition={prefersReducedMotion ? {duration: 0} : {opacity: {delay: 0.4, duration: 0.4}}}
+ // honoured here, for all of them. The empty `default` keeps Motion's own per-value
+ // defaults for the size, shadow and scale animations, which otherwise resolve to the
+ // opacity transition and lose their timing.
+ transition={
+ prefersReducedMotion
+ ? {duration: 0}
+ : {default: {}, opacity: {delay: 0.4, duration: 0.4}}
+ }
animate={[
loading ? 'background' : 'active',
reloading ? 'reloading' : 'idle',You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit 154271f. Configure here.
Review: request changesOne blocking issue (inline on Verified
BlockingThe opacity-only Non-blocking
VerdictRequest changes until the transition no longer leaks into the other values and a test covers it. Everything else is ready. |
154271f to
67edd84
Compare
Re-review (67edd84): approveThe blocking issue is fixed and its thread is resolved. Verified
VerdictApprove. |
67edd84 to
cf4fe06
Compare
cf4fe06 to
de248d6
Compare
de248d6 to
4822e42
Compare
…dary shows it again
With `beta.reactActivityMode` the Studio keeps recently used tools mounted behind
`<Activity>`, which detaches and re-attaches refs around a hidden period. Motion treats a
re-attached element as a remount and replays `initial` → `animate`, so returning to a
document's preview pane faded the iframe out and back in over 800ms (the `active`
variant's `[0, 0, 1]` opacity keyframes start from 0).
Start the iframe in its `animate` state (`initial={false}`; its first state is
`background`, the same as the old `initial`) so there is nothing to jump back to, and
express the loaded fade-in as a delayed transition on the `active` variant instead of
keyframes, so the replay animates from the current opacity to the same opacity and changes
nothing. Keeping the transition on the variant leaves the size, shadow and reload pulse on
Motion's defaults (a `transition` prop would replace them for every value). A variant
transition also replaces the `MotionConfig` one, so `active` is a dynamic variant that
gets `prefersReducedMotion` through `custom` and leaves the transition out when reduced
motion is preferred, falling back to the `{duration: 0}` config.
Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>
4822e42 to
8ff619a
Compare


Description
With
beta.reactActivityMode, returning to a document whose preview pane was open faded the iframe out and back in over ~800ms. The Studio keeps recently used tools mounted behind<Activity>, which detaches refs while a tool is hidden and re-attaches them on reveal; Motion (14.1) treats a re-attached element as a remount on purpose: it jumps the element's values back toinitialand replaysinitial→animate. The iframe'sactivevariant animated opacity with[0, 0, 1]keyframes, so every reveal started it from 0 again, with the preview invisible for the first half of the animation.The iframe now starts in its
animatestate (initial={false}): its first state isbackground, which is exactly what the oldinitialset, so the first load looks the same, but there are no initial values to jump back to on a remount. The loaded fade-in is expressed as a delayed transition instead of keyframes, so a replay animates from the current opacity to the same opacity and changes nothing. If the iframe finished loading while the tool was hidden, the reveal still fades it in, since Motion animates from the current value.The transition lives on the
activevariant itself, so thedesktop/mobilesizes, the box shadow and thereloadingscale pulse keep Motion's defaults as before (atransitionprop on the element would replace them for every value, not only the one it names). A variant transition also replaces theMotionConfigdefault, soactiveis a dynamic variant: it receivesprefersReducedMotionthroughcustomand leaves the transition out when reduced motion is preferred, which falls back to the{duration: 0}fromMotionConfig.IframeInneralready readsusePrefersReducedMotion()for that config and passes the boolean down toFrame.Why not block the replay with
<AnimatePresence initial={false}>: Motion applies the block before it applies the currentanimatevalues, so a preview that finished loading while hidden would come back invisible.What to review
Iframe.tsx:initial={false}andcustom={prefersReducedMotion}on themotion.iframe, and the dynamicactivevariant (delay: 0.4, duration: 0.4matches the hold-then-fade of the[0, 0, 1]keyframes, which Motion ran over its 0.8s keyframe default).FrametakesprefersReducedMotionas a prop.package.json/pnpm-lock.yaml:@testing-library/reactandjsdomascatalog:devDependencies for the test (the lockfile change is the two importer entries).Affects
sanity-plugin-iframe-pane.Testing
Iframe.test.tsx(jsdom, Studio hooks mocked):1after an<Activity>hide/show cycle (onmainit is0: Motion has jumped it back toinitialand holds it there for the first 0.4s of the replay);transformis stillnone350ms after clicking Reload and reachesscale(0.98)afterwards (with an opacity-onlytransitionprop on the element, which strips the defaults from every other value, it was atscale(0.98)by 300ms);1within 100ms instead of after the 0.4s hold, and switching to the mobile viewport puts it at414pxright away.main: opacity holds0until ~400ms and reaches1at ~800ms on both; after Reload,transformstaysnonethrough 500ms and reachesscale(0.98)at ~805ms on both; the width change to414pxcompletes at ~300ms on both.pnpm --filter sanity-plugin-iframe-pane exec vitest run,pnpm build,pnpm lint,pnpm knip,pnpm formatare clean.dev/test-studio(Structure,iframeExampledocument on its Preview tab,reactActivityModeon) with a headed Playwright harness that switches to Vision, waits 7s, switches back and records DOM mutations and painted frames (CDP screencast) until the page is quiet for 30 frames. Before: the iframe's style went toopacity: 0at +194ms and back to1at +1004ms, 46 painted frames and the final visual state reached at frame 39 (+755ms). After: no opacity mutation on the iframe, 25 painted frames, final visual state at frame 12 (+195ms); what remains is the Structure tool's own document list and status bar re-rendering on reveal, the same as the control document without this pane (36 frames, +753ms).Preview pane before: the iframe fades out and in on reveal
Preview pane after: the iframe stays put
Notes for release
Fixes the preview iframe fading out and back in each time you return to the Structure tool in a Studio with
beta.reactActivityModeenabled.To show artifacts inline, enable in settings.