fix(discussions): emit CSLP_TAGS_UPDATED after variant switch so comment icons mount at correct positions#583
Open
karancs06 wants to merge 1 commit intodevelop_v4from
Conversation
…ent icons mount at correct positions
When the visual editor switches variants it sends GET_VARIANT_ID to the
iframe SDK. The user's CSR app then re-renders with the new variant data,
changing every data-cslp attribute value (e.g. from
content_type.uid.locale.field to v2:content_type.uid_variantId.locale.field).
Previously the visual editor had no way of knowing when those attributes
had settled, so it would re-mount comment icons against stale CSLP values —
icons either missed their elements entirely or were positioned using
getBoundingClientRect() on the pre-variant DOM and then failed to track
scroll because updateHighlightedCommentIconPosition could no longer find
the element by the now-stale field-path attribute.
Changes
───────
src/visualBuilder/utils/types/postMessage.types.ts
• Add CSLP_TAGS_UPDATED = "cslp-tags-updated" to the post-message enum.
This is the signal the SDK sends back to the visual editor once the
iframe's data-cslp attributes have settled after a variant switch.
src/visualBuilder/eventManager/useRecalculateVariantDataCSLPValues.ts
• updateVariantClasses now accepts an optional onSettled callback.
• Inside the existing MutationObserver (which already watches each
[data-cslp] element for attribute changes), scheduleSettled() is
called on every relevant mutation. After a 200 ms debounce with no
further mutations, onSettled fires — meaning the burst of re-render
updates has finished.
• A 1500 ms fallback timer fires onSettled regardless, so entries that
have no variant field overrides (zero mutations) still unblock the
visual editor.
• hasSettled flag ensures onSettled is called at most once per
updateVariantClasses invocation, even if both the debounce and the
fallback race.
src/visualBuilder/eventManager/useVariantsPostMessageEvent.ts
• GET_VARIANT_ID handler (CSR path): passes an onSettled callback to
updateVariantClasses that sends CSLP_TAGS_UPDATED once the DOM settles.
• GET_VARIANT_ID handler (SSR path): sends CSLP_TAGS_UPDATED immediately
after addVariantFieldClass because SSR classes are applied synchronously
on the existing DOM — no re-render delay exists.
• Removed accidental console.log left from debugging.
src/visualBuilder/generators/generateHighlightedComment.tsx
• updateHighlightedCommentIconPosition: when a scroll/resize position
update is attempted for an icon whose [data-cslp="${path}"] element
is no longer found, the icon is removed from the DOM instead of being
silently skipped. Without this, an icon mounted just before the
variant re-render would drift to the wrong fixed position and stop
tracking scroll permanently. The visual editor will re-mount it
correctly once CSLP_TAGS_UPDATED arrives.
• Removed accidental console.log left from debugging.
src/visualBuilder/eventManager/__test__/useVariantsPostMessageEvent.spec.ts
• Four new tests in the SSR-handling describe block covering:
1. CSLP_TAGS_UPDATED sent immediately for SSR + variant provided.
2. CSLP_TAGS_UPDATED sent immediately for SSR + null variant.
3. updateVariantClasses called with an onSettled callback (CSR).
4. CSLP_TAGS_UPDATED sent when the onSettled callback fires (CSR).
src/visualBuilder/eventManager/__test__/useRecalculateVariantDataCSLPValues.spec.ts (new)
• Four tests for the onSettled callback logic:
1. Fires via the 1500 ms fallback when no mutations occur.
2. Does not fire before 1499 ms.
3. Fires exactly once even if the fallback would fire again.
4. Does not throw when no callback is provided.
src/visualBuilder/generators/__test__/generateHighlightedComment.test.ts (new)
• Five tests for updateHighlightedCommentIconPosition and
removeAllHighlightedCommentIcons covering: position update when target
exists, orphan removal when target is gone, mixed-case handling, empty
DOM safety, and full bulk removal.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔒 Security Scan Results
⏱️ SLA Breach Summary
✅ BUILD PASSED - All security checks passed |
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When the user switches variants in the Visual Editor, the iframe's CSR (client-side rendered) app receives the new variant and asynchronously re-renders, mutating every
data-cslpattribute in the DOM from the base CSLP format (content_type.uid.locale.field) to the variant format (v2:content_type.uid_variantId.locale.field).The Visual Editor's
DiscussionPagewatches thevariantIdchange and immediately callshighlightCommentsOnCanvas, which callshighlightCommentIconOnCanvasfor each active discussion field path. That function:[data-cslp="${fieldPath}"]to find the target elementgetBoundingClientRect()to place the icon atposition: fixedRace condition: When the visual editor fires highlights the iframe DOM has not yet settled — the
data-cslpattributes still carry the old values. Two failure modes result:data-cslpvalue; subsequent scroll events callupdateHighlightedCommentIconPosition, which silently skips any icon whose target is now missing, leaving the icon permanently drifted.Solution
1.
updateVariantClasses—onSettledcallback (useRecalculateVariantDataCSLPValues.ts)updateVariantClassesalready uses aMutationObserverto re-apply variant CSS classes asdata-cslpattributes change. We extend it to accept an optionalonSettledcallback that fires once the DOM has settled:scheduleSettled()— called inside the MutationObserver callback after each batch of mutations; starts/restarts aCSLP_SETTLED_DEBOUNCE_MS(200 ms) debounce timer. When no further mutations arrive within the window the callback fires.CSLP_SETTLED_FALLBACK_MS(1 500 ms) ensuresonSettledalways fires even when no mutations are observed (e.g. SSR-hydrated pages that have already settled by the time the observer attaches).hasSettledflag — guarantees the callback is invoked exactly once regardless of whether the debounce or the fallback fires first.2.
useVariantFieldsPostMessageEvent— sendCSLP_TAGS_UPDATEDsignal (useVariantsPostMessageEvent.ts)The
GET_VARIANT_IDhandler now passes the settled callback through:The new
CSLP_TAGS_UPDATED = "cslp-tags-updated"postMessage event constant is added topostMessage.types.ts.3. Orphan cleanup in
updateHighlightedCommentIconPosition(generateHighlightedComment.tsx)Previously, when
updateHighlightedCommentIconPositionprocessed a scroll/resize event it silently skipped any.highlighted-commenticon whose[data-cslp]target element was no longer in the DOM — leaving permanently drifted icons after a variant switch.The visual editor re-mounts all icons fresh when
CSLP_TAGS_UPDATEDarrives, so removing orphans here is safe.Tests added
useRecalculateVariantDataCSLPValues.spec.ts(new)useVariantsPostMessageEvent.spec.tsCSLP_TAGS_UPDATEDsent immediately for SSR (variant/null);updateVariantClassescalled with callback for CSR;CSLP_TAGS_UPDATEDsent when callback firesgenerateHighlightedComment.test.ts(new)removeAllHighlightedCommentIconsChecklist
onSettledfires exactly once (idempotency guard)🤖 Generated with Claude Code