Overview
The active_version of a Component config entity can change due to a number of reasons, such as:
- A code update that includes changed schema for an SDC.
- Within the Canvas UI, a Code Component author changes their prop or slot definitions (once #3509115: [META] Plan to allow editing props and slots for in-use code components makes that possible).
- A module that implements
hook_canvas_storable_prop_shape_alter()is installed or uninstalled.
#3463996: [META] When the field type, storage/instance settings, widget, expression or requiredness for an SDC/code component prop changes, the Content Creator must be able to upgrade is tacking more difficult cases, but there are quite a few cases where the change to the active_version has 0 repercussions for component instance data saved for an older version:
- An optional prop is added
- A slot is added
- A prop was changed from required to optional
- The
default_valuewithin one of theprop_field_definitionsgot changed - The example values of a slot got changed
For all of the above cases, we can update the component instances to the new component version without having to do anything other than simply marking them with the new version.
Proposed resolution
- Whenever a component instance is edited (TBD for exactly which UI interaction / API call this refers to), if it's currently referencing an old component version, and the component's active version consists of solely the changes listed above, then automatically update its version marker to the new version upon saving the data to auto-save storage.
- Note that for this issue, we do not need to bulk update multiple entities. We can simply do it one entity at a time when that entity is edited.
User interface changes
None — other than new image media types appearing immediately on all existing component instances that have "image" props:

| Comment | File | Size | Author |
|---|---|---|---|
| #23 | instantly more image media types.gif | 1.95 MB | wim leers |
Issue fork canvas-3567413
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
Comment #2
wim leers👏 This is a great first step!
It avoids ALL complex cases. Best of all: all the necessary metadata is already present in
Componentconfig entities' per-versionfallback_metadata.slot_definitionsandsettings.prop_field_definitions.But the latter … only exists for SDCs and code components (and any other component sourced from a
ComponentSourcethat usesGeneratedFieldExplicitInputUxComponentSourceBase). →Citing
type: canvas.generated_field_explicit_input_uxComment #3
wim leersPseudo code:
Once that exists, calling something like this on every component instance in a component tree when loading it for editing in the Canvas UI would be sufficient:
(That one is not pseudo code, this should actually work with zero changes.)
Comment #4
penyaskitoI had a similar approach in mind. Preventing what's to come later, and hopefully not over-engineering, I had planned:
isUpdateNeeded(ComponentTreeItem $component_instance),canUpdate(ComponentTreeItem $component_instance)andupdate(ComponentTreeItem $component_instance)GeneratedFieldExplicitInputUxComponentInstanceUpdaterwith what you had in mind.updateInteractively(ComponentTreeItem $component_instance), for those cases which can't be automatically updated (e.g. should a new required prop have the default value? or the user needs to select it?)updatecan be called when the component tree goes from autosave → publish (e.g. inpreSave(), as with the existing upgrade paths).updateInteractively, or we can try a PoC doing that when an existing component tree is → autosaved. In both cases the editor will be able to review, as the preview will be updated.Thoughts?
Comment #5
wim leers🥳
updatershould be renamed toauto_updaterortransparent_updateror something like that. That'd leave room for a futureinteractive_updaterhandler. WDYT?Comment #6
nagwani commentedComment #9
penyaskitoI thought having
$fromand$toarguments. But then realized: what's the point? I want to have the latest (active) version. What's the point if I can't? The only benefit I can think of is if we at some point "clean up" unused versions from Component config entities, as it will reduce the number of used versions. Is it worth given the complexities it adds?Maybe I picked a bad name. Let's say I add a required prop with a default value. I can upgrade automatically, but I'd still want the editor to review the outcome of this upgrade is what they expect. That's not in scope here, but I thought of this because I initially was thinking about naming this
auto_updatertoo. But don't think we want to have 2 different handlers in the future when/if we support that. That's why I would go with a more genericupdaterMakes sense. Probably _in addition_ to what I said (to cover if I already edited something, it's in autosave, then I click publish AFTER the component has changed again but still allowing a non-interactive upgrade).
Comment #10
penyaskitoDiscussed with @attilatilman and I’m taking over.
Comment #11
wim leersThat we'd minimize the number of
Componentconfig entity versions being used by the site's component instances.Benefits:
I'm fine with punting it to a follow-up. This alone is a huge leap forward. But then we should document the rationale for NOT doing this yet in the (code) docs. 🙏
Aha!
So: this would NOT be applied automatically during
::getComponentTree(); it'd only be applied while loading the component instance's form? 🤔Comment #12
wim leersDetailed review: https://git.drupalcode.org/project/canvas/-/merge_requests/485#note_665972
Comment #13
penyaskitoThat was my first attempt, but:
So doing it in
:getComponentTree();worked. We need to find a less aggressive way of doing that. Checking ifnormalizeForClientSideis viable is the next try.Comment #14
penyaskitoEven better.
::getClientSideRepresentation. See https://git.drupalcode.org/project/canvas/-/merge_requests/485/diffs?com...And https://git.drupalcode.org/project/canvas/-/jobs/8323270 failures indicate what I would have expected: we don't need lots of new test coverage, but ensuring the existing
JsComponentEvolutionTesttakes this into account (and at least is partially affected)!Comment #15
penyaskitoReady for reviews (manual testing also encouraged)
Comment #16
wim leers🏓 https://git.drupalcode.org/project/canvas/-/merge_requests/485#note_669327
Comment #17
penyaskitoCreated #3571362: [PP-1] When editing a Canvas entity with a component tree (e.g., page, template, or region), automatically upgrade component instance versions to the most recent possible version where there's no impact on existing data
Comment #18
penyaskitoCreated #3571366: Allow Canvas Patterns+PageRegions+ContentTemplates provided by Recipes to NOT specify component versions
Comment #19
penyaskitoCovered everything but
I don't see a clean way forward. Maybe tomorrow.
Assigning in the meantime for the rest.
Comment #20
wim leersReview
GeneratedFieldExplicitInputUxComponentInstanceUpdaterTestis 🤩👏 (and it's >50% of the MR!)What I did on the MR
Next up
MR already approved — it's now in the hands of @penyaskito. 😊 I don't feel the need to chime in further.
Zooming out: why did I push back?
I focused on data integrity/tech debt/long-term thinking.
The 3 chief concerns in my review(s):
Comment #21
wim leersI thought I had missed 2 test cases:
But neither is possible/allowed thanks to config dependencies: a
Componentrelying on a media type (in any version of the Component config entity!) has a config dependency preventing such in-use media types from being deleted.(The ability to safely delete unused media types at a later time would be a task/feature request for Canvas on its own — it'll require refining
::onDependencyRemoval()to check if all of the affected component versions have zero instances referring to it.)Comment #22
wim leersManually tested: works great. Tested:
drush cr, edit component instance ⇒ new prop appears!In doing so, I discovered one caveat for usability though: already auto-saved component trees do not get this automatic update. The automatic update works only when starting to edit a component tree for "the first time", not when continuing from a prior auto-save.
It is likely a user will get confused by this at some point. It is explainable, and it is possible to recover from, so I think it's fine as-is 👍🚢
Comment #23
wim leersGIF showing the instantaneous appearing of new image media types:

Yay for #3547579: Introduce a new cache tags aware prop shape repository, so changes affecting prop shape calculation can force the re-invoke of hook_canvas_storable_shape_prop_alter in December!
Comment #24
wim leersActually, this worked fine, and it still does. Mea culpa!
Comment #25
wim leersI think this merits a change record? We should inform Canvas users (especially the advanced ones) that this frustration is now largely a thing of the past. It can include the GIF because it's the easiest-to-grok (because 100% UI-driven) change.
Or perhaps you want to do one over-arching change record for #3463996: [META] When the field type, storage/instance settings, widget, expression or requiredness for an SDC/code component prop changes, the Content Creator must be able to upgrade?
Comment #26
heyyo commentedI will just say one word !
Youhou !
Comment #27
penyaskitoDraft change record: https://www.drupal.org/node/3571790
Comment #29
penyaskito🥰🎉🚢🎂🤩🚀