Problem/Motivation

Editing an already-inserted component never refreshes its live preview. The widget keeps showing the previous render. The new value is saved correctly, and only becomes visible after saving the node and re-opening the edit form, which is what makes the bug look like a caching problem rather than a plugin one.

Found on SDC Embed 1.1.1. The 1.x HEAD on git.drupalcode.org carries the same code byte for byte, so the bug is present there too.

Steps to reproduce

  1. Enable SDC Embed, add the "Embed component" button to a text format's toolbar, enable the sdc_embed_filter on that format, and leave live_preview: TRUE in sdc_embed.settings.
  2. Edit a node using that format and insert a component.
  3. Double-click the embedded widget (or select it and press the "Embed component" button), change any prop in the dialog, and submit the dialog.

Expected: the widget's preview re-renders with the new prop value.

Actual: the widget keeps showing the previous render.

Root cause

On the edit path InsertSdcEmbedCommand::execute() does not recreate the model node; it calls writer.setAttribute() on the four data-sdc-* attributes of the already-selected drupalSdc element. Because the editing downcast is a plain elementToElement() converter, nothing reconverts the widget, so the only thing that can repaint it is the change:data listener in SdcEmbedEditing::init(). That listener walks differ.getChanges() and looks the element up like this:

if (change.type === 'attribute') {
  const parent = change.range?.start?.parent;
  return parent && parent.is('element', 'drupalSdc') ? parent : null;
}

That is the wrong end of the range. For an attribute change on an element, the Differ files the change under the element's parent and reports a range whose start sits in that parent, immediately before the element. So range.start.parent is the parent (typically $root) and the element is range.start.nodeAfter.

range.start.parent would only be the element if the attribute had been set on a text node inside it, which cannot happen here, since drupalSdc is registered as isObject/isBlock and holds no text. The condition is therefore never true, _refreshPreview() never runs, and the editing view keeps the stale HTML.

Corroboration

CKEditor 5 itself draws exactly this distinction in createChangeReducer() in ckeditor5-engine's src/conversion/downcasthelpers.ts, which the downcast helpers register on the dispatcher's reduceChanges event:

const i = "attribute" == e.type ? e.range.start.nodeAfter : e.position.parent;

Why insert and reload appear to work

Insertion takes the change.type === 'insert' branch, which is correct, and is additionally covered by the setTimeout(() => this._refreshPreview(modelElement), 0) scheduled from the editing downcast converter. Saving and re-opening the node works because the upcast recreates the model element, which runs the editing downcast again and re-schedules that same first preview. Only the in-place attribute update, which is the actual edit flow, is broken.

Proposed resolution

In the comments.

Remaining tasks

Review.

User interface changes

None.

API changes

None.

Data model changes

None.

Issue fork sdc_embed-3620850

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

luissousa21 created an issue. See original summary.

luissousa21’s picture

Issue summary: View changes

luissousa21’s picture

Status: Active » Needs review

MR !2.

_findSdcElement() reads change.range.start.nodeAfter for attribute changes instead of .parent. _affectsSdcElement() folds into it, since both carried the same wrong expression and always ran as a pair, and the change.name === 'drupalSdc' test moves onto the insert branch.

The editing downcast is left alone on purpose rather than moved to reconversion. The file's header comment says replacing only the sdc-embed-body child is deliberate, to keep the selection and caret and avoid a flicker on every update.

Checked on a clean 11.4.6 install with the demo submodule. Before: change a prop and the model holds "value":22 while the widget still shows 11. After, it repaints. One dialog submit still fires a single POST to the preview endpoint, so the 250 ms debounce in schedulePreview() already covers the attribute changes.

phpcs clean, phpunit 30 tests / 131 assertions green, eslint no errors. The phpstan, stylelint and cspell findings are all pre-existing; this only touches js/sdc-embed.js.

c2h2o5 made their first commit to this issue’s fork.

  • c2h2o5 committed 9b12fc65 on 1.x authored by luissousa21
    Issue #3620850: Resolve the changed element from range.start.nodeAfter...
c2h2o5’s picture

Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.