Overview
Follow-up to #3567413: When editing a Canvas entity with a component tree (e.g., page, template, or region), automatically upgrade component instance versions for cases where there's no impact on existing data. After that issue lays the initial groundwork by handling the cases listed there, this issue is for adding support for two other cases:
- A Component's active version does not have a prop that the component instance's (old) version has (i.e., a prop got removed from the component).
- A Component's active version does not have a slot that the component instance's (old) version has (i.e., a slot got removed from the component).
For the cases above, we need to handle both:
- Upgrading the component instance's version when the component instance is edited (same as in #3567413: When editing a Canvas entity with a component tree (e.g., page, template, or region), automatically upgrade component instance versions for cases where there's no impact on existing data).
- Rendering component instances that haven't been upgraded yet (haven't been edited since the Component got changed).
Proposed resolution
Improve the updater t support removed props and slots.
- Upon upgrading the component instance, remove values of props that don't exist in the component's active version.
- Upon upgrading the component instance, remove child component instances from slots that don't exist in the component's active version. This requires modifying the entire component tree.
Removed from scope, as rendering components with extraneous data does not break rendering. When the component is upgraded, data will be cleaned up.
When rendering a not-yet-upgraded component instance (component instance's version not equal to the component's active version), remove prop values and slot children from the ephemeral component instance data prior to rendering. In other words, when rendering, don't pass prop and slot values that aren't expected (aka supported!) by the component's code.
This issue's scope DOES NOT include bulk upgrading all component instances (i.e., deleting orphaned data from all component instances). It only covers upgrading component instances when they are edited.
A consequence of the above is that if someone removes a prop/slot from their component, then adds it back a short while later, component instances that haven't been edited in the meantime continue functioning as before (their old prop/slot values were never removed, so effectively get restored). Ensure there's test coverage for this, whether that's added in this issue or being added in other issues such as #3557272: BE: Support removing a slot from a code component.
User interface changes
- When a prop or slot is removed from a component, published pages will start rendering without them.
- If/when a prop or slot of the same name is re-added, component instances that had been edited in the meantime will have had that data removed already, but component instances that had not been edited in the meantime will start rendering with that old data again.
Issue fork canvas-3568906
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 leersJust like at #3568602-2: Handle upgrading and rendering not yet upgraded component instances of components with new(ly) required props that include an example value: beautiful issue summary! Thanks, @effulgentsia! 💙
AFAICT the "removed prop" part of this issue is already being discussed/addressed as part of #3567413: When editing a Canvas entity with a component tree (e.g., page, template, or region), automatically upgrade component instance versions for cases where there's no impact on existing data? 🤔
Do we want to reduce the scope of this issue to just "removed slots"?
+1 for thorough test coverage for this. 🙏
😮🤔 This is the BIG one! This could quite literally undo/lose significant amounts of carefully crafted content.
Are we sure that this is the approach that we want? We could alternatively do something like (pseudocode):
👆 That would:
Comment #3
lauriiiI understand the concern about potential data loss, but I believe the simpler approach is the right one. Deleting the orphaned components aligns with established patterns. When you delete a folder, its contents are deleted. When you remove a field from a content type, the data is deleted. Or in a database schema, when you drop a column, the data is gone.
This is also how Drupal's existing page builders handle the same scenario. In Layout Builder, inline blocks that are removed from a layout due to a region being removed, become orphaned and get cleaned up asynchronously. When a paragraph field is removed from a content type, the paragraph entities may linger in the database temporarily, but get deleted later by garbage collection.
In both cases, the philosophy is: stop rendering removed content, then clean it up. Neither system attempts to "rescue" orphaned content by creating new visible containers. I'm concerned that creating sibling components would likely confuse content authors more than help them. When a developer removes a slot from a component, that's a deliberate decision. Because we only upgrade on edit, there's already a grace period. If someone removes a slot then realizes it was a mistake and adds it back, component instances that weren't edited retain their data. This provides a reasonable recovery window without adding complexity.
Comment #4
wim leersAlright, if it's intentional, all good! 👍 Thanks for confirming!
Comment #7
mglamanComment #8
mglamanGotta fix some tests
Comment #9
mglamanComment #11
mglamanChatted with @penyaskito and @effulgentsia. We're going to drop anything related to rendering, since it's safe to render the component with extraneous data (old prop inputs and old slots) until the component is upgraded.
We're removing this part of the solution:
This item still remains valid
Comment #12
mglamanClean up the proposed solution a bit with our de-scope of handling anything with rendering. We can worry about that in #3568602: Handle upgrading and rendering not yet upgraded component instances of components with new(ly) required props that include an example value which has actual consequences when rendering (new required props.)
Comment #13
wim leersMR looks great overall! 👏
I have data loss concerns about the auto-update-upon-publishing-auto-saves that this MR introduces. See MR for details.
Comment #14
mglamanI think this is all set for the next review! Data loss issues reported in #13 should be mitigated, that approach was removed
Comment #15
mglamanMarking RTBC with penyaskito approval, but I want wim leers ++ based on data concerns in an earlier approach.
Comment #16
effulgentsia commented+1 to merging this. Looks like all the critical concerns were addressed. The remaining open MR threads can be followups.
Comment #18
mglamanLeaving to `to be ported` in case there is follow ups to address, which I can handle in #3568602: Handle upgrading and rendering not yet upgraded component instances of components with new(ly) required props that include an example value
Comment #19
wim leersReviewed retroactively per #15.
This looks amazing — thanks to the STR by @penyaskito, @mglaman nicely researched & elegantly solved the key challenge this MR surfaced. 👏
Needs follow-up for:
And just to be safe: @mglaman: can you confirm https://git.drupalcode.org/project/canvas/-/merge_requests/540#note_690262?
Comment #21
penyaskitoComment #23
wim leersNow needs @lauriii to confirm the second bullet in #19.Nope, per @penyaskito's response to the last link in #19, it looks like we're missing test coverage for an edge case. Can you provide STR, @penyaskito?
Comment #24
lauriiiIt would be preferable to not show anything as changed when applying updates because we're applying the same update during run time so from the perspective of an end-user, nothing has changed. I suggested that if we continue on the current path, we should at least show a toast to inform that the content was updated so that they know why it shows up as changed.
Comment #25
penyaskitoI'm afraid I cannot reproduce that as is. However found a different issue (testing with branch at #3568602: Handle upgrading and rendering not yet upgraded component instances of components with new(ly) required props that include an example value, but shouldn't be affecting this) which might be related. I've created #3574026: Component instances are not updating in the same way in regions than other entities to check this.
tl;dr: everywhere in Canvas we are previewing auto-save stuff, but not for page_regions, where we ignore auto-saved stuff and use published versions.
Should this have a change record?
Comment #26
penyaskito#24: That's not correct. Updates don't happen on runtime. If your changes are done in a non-BC way, your published page is broken.
Comment #27
wim leersI think https://www.drupal.org/node/3571790 covers it? I just added this issue to that CR. Adding more detail to the CR is welcome but not essential IMHO.
Thanks for #3574026: Component instances are not updating in the same way in regions than other entities!
Comment #29
wim leersCross-posted with #26.
I propose the two of you meet to get to the bottom of this 🙏
Comment #30
penyaskito#27: We didn't specify which component changes are upgradable, so no change records needed then.
Comment #31
mglamanThe bottom of what? This had laurii feedback and approval on the proposed solution.
Comment #32
wim leers@mglaman, you wrote:
— https://git.drupalcode.org/project/canvas/-/merge_requests/540#note_689199
I asked Lauri to confirm that. 😊
AFAICT he basically did confirm it's fine in #26 but requested a follow-up? ( — tagging for that.)
But @penyaskito in #24 pushed back against @lauriii in #26. @penyaskito DM'd me about this: he's concerned about a mismatch in expectations. That is what I suggested the two of them to meet about.
Comment #33
penyaskitoThat's true here.
Isn't true for #3568602: Handle upgrading and rendering not yet upgraded component instances of components with new(ly) required props that include an example value, which is what I misunderstood.
Apparently we still need some """"updates"""" (quotes intended) for rendering with new required props.
But for #24: They won't be the same updates. They would be just adding required prop values default values, not the entire update (e.g. deleting removed slots contents, deleting removed prop values)
Comment #34
penyaskitoComment #35
penyaskitore #24: created #3574253: Flag Canvas component trees that have been auto-updated on pending changes API call and #3574257: Provide a toast informing editors about which Canvas component trees have been auto-updated.
Thanks all!
Comment #37
wim leersAFAICT the bug that @mayur-sose reported at #3574743: 500 error for code component preview after deleting required prop from in-use Code Component is strongly connected to this issue.