Overview
Requirement list for Experience Builder in row 5 has item defined as “Place blocks as components” (link to spreadsheet). Accompanying this requirement is the following user story:
As a builder, I can place and render blocks and fields rendered via Twig if they are exposed to the page builder as components. Blocks and fields defined in Twig should be able to render both in the preview, and the end-user facing page. This is to enable backwards compatibility with code that already exist in contrib and custom modules.
Currently Component entity is tightly coupled to SDCs, this is being partially resolved in #3469609. However, the Component entity will still be directly tied in 1:1 relationship with single directory components that meet Experience Builder requirements.
Proposed resolution
There seems to be general consensus between @larowlan, @wim-leers, @effulgentsia, @tedbow, @longwave and @fmazeikis that we for we should retain Component entity type, but it needs to be able to function as translation layer between different types of sources, for example - SDCs, Blocks, Layouts and Paragraphs.
@longwave have put it pretty well in 3454519#comment-15778070:
We could try to repurpose SDCs so they can wrap blocks or layouts, but this doesn't conceptually appear to be the right thing to do - SDCs are self contained single directory components by definition, so expanding them with features beyond is scope creep.
We could try to use blocks everywhere and wrap SDCs in blocks, but then we run into the slots issue, and would have to extend blocks so they support slots in some way.
What we are trying to do here is map props/values and slots/components into (at least) three things that are conceptually different (SDCs, blocks, layouts). This is effectively a translation layer and as such should be entirely standalone from the sources and targets of the translation; therefore I think this is the correct direction for this problem.
This issue will adapt some of the code proposed by @larowan in #3454519. Specifically, we will introduce this part of proposal:
Component source plugin
This decouples the component config entity and component tree from SDC components. There's a SingleDirectoryComponent implementation that all the existing logic moves to Adds two additional implementations as follows:
- Blocks - this will support blocks (e.g. views, menus) as well as a bridge to layout builder
This issue will intentionally not introduce the rest of the proposals of #3454519 in order to limit the scope of the issue to blocks. This issue will only add the initial set of "ComponentSourcePlugins" - "SDC Source Plugin" and "Block Source Plugin".
This doesn't mean this implementation is final and other Source Plugins may be introduced in separate issues or the entire approach could change in the future.
This issue will only allow fully validatable blocks, and blocks that are allowed in Settings Tray, to be placed in the UI.
User interface changes
For now, block plugins appear under the main "Default components" section in the UI, but in a future issue they will be moved to a separate "Dynamic components" section.
Issue fork experience_builder-3475584
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
f.mazeikis commentedComment #3
wim leersIs it correct to state that #3469609: Prepare for multiple component types: ComponentTreeStructure should contain Component config entity IDs, not SDC IDs is a blocker for this?
Comment #5
f.mazeikis commentedI wasn't marking it as blocking as it looked as if the issue would be merged anyway and we could have started this issue while the work continued on #3469609. And it is merged now.
I've moved most of the code from MR68 (closed) by @larowlan and reached out to him on slack as I couldn't retain his commit signature.
This is not a working commit and will be used as starting point.
Comment #7
wim leersDid an initial high-level review. Looks to be on the right track! 😄 👍 I bet @larowlan will be pleased 😁
Thanks for getting this going! 🙏
Marking needs work for the following high-level bugs:
BlockInterface(which is for Block config entities) versusBlockPluginInterface(which is for block plugins). We need only the latter.expose_settingsis, and AFAICT it cannot result in validatable XBComponentconfig entitiesdefaultsshould be moved into the SDC source-specific settingsComment #8
wim leers#3469609: Prepare for multiple component types: ComponentTreeStructure should contain Component config entity IDs, not SDC IDs landed ~2 weeks ago. Updating title.
@f.mazeikis just discussed this issue for an ~hour. This comment captures the essence of that meeting.
Docs
Tagging because besides an initial implementation, the most crucial part here is that @f.mazeikis documents how he envisions how this will all work. IOW: an equally imporant goal for this issue is to update
docs/components.mdanddocs/config-management.md, which were introduced by #3468112: Document the current component discovery + SDC criteria + `Component` config entity, and describe in an ADR.Implementation
The initial implementation can be very limited — we shouldn't try to solve everything all at once here. I just discussed this with @f.mazeikis and we agreed on the following high-level points, with more detail/nuance to be defined by @f.mazeikis in the coming days:
Componentconfig entities for block plugins just like it does for SDCsComponentconfig entity is sufficient. The simplest one:\Drupal\system\Plugin\Block\SystemPoweredByBlock(which has zero settings). Ideally, there would be one more:\Drupal\search\Plugin\Block\SearchBlock(which has a single setting that was made fully validatable in #3379725).A special case is
\Drupal\block_content\Plugin\Block\BlockContentBlock, because it is a (derived) block plugin that depends on content entities. It's crucial in Layout Builder, for which XB eventually must provide an upgrade/migration path. We think we see a way forward for that special case too. Details TBD.Comment #9
wim leersAlso changing the parent issue to get this listed at https://contribkanban.com/board/experience_builder, and hence increase visibility.
Comment #10
wim leersComment #11
wim leersDiscussed with @longwave, he'll be pushing this forward right after he finishes #3469610: Prepare for multiple component types: prefix Component config entity IDs with `sdc`, which blocks this anyway.
@f.mazeikis did a walkthrough/hand-over with me last Friday, before he went on vacation, so I'll be able to provide reviews 👍
Comment #12
wim leers#3469610: Prepare for multiple component types: prefix Component config entity IDs with `sdc` landed!
Comment #14
wim leers🏓 AFAICT we're on the same page, @longwave :)
Comment #15
wim leers#3480217: Add Kernel test coverage of ApiLayoutController is in, which AFAIK should help with getting this MR ready :)
Comment #16
longwaveThe MR is green. "Search form" is available as a component and the preview works (ish) but after dragging it to the canvas the preview fails with an error; going to continue working on this while looking for reviews of the rest of the changes.
Comment #17
wim leers#16 🥳
Reviewing in-depth! Docs definitely have been updated, removing tag 👍
Comment #18
wim leersFirst complete review pass posted!
Comment #19
longwaveThanks for the review! I've run out of time now to work on this, so unassigning; I've addressed some of the feedback and added a bunch of @todos but there are still some things to be done - replied to some of the MR comments on those points. Tagging to add test coverage as well as we should add some here to prove that blocks work to some extent.
Comment #20
wim leers#3482529: Refactor XB internals to not assume /xb-components HTTP API returns only SDC-powered XB Components is at minimum a soft blocker.
Comment #21
wim leersIn DM, @f.mazeikis asked what the point is of keeping the
sourceproperty around separately, if it's already part of the ID prefix.Having
sourceseparate makes it easy to validate, plus it's relevant information inside the config entity.The ID's naming structure might still change!
Fortunately, over at #3324140: Convert field_storage_config and field_config's form validation logic to validation constraints, I already worked on a
StringPartsconstraint that simplifies that. Pushing commits to transplant that into XB — because that core issue has not moved forward in a very long time, but that constraint definitely is solid.Comment #22
longwaveAfter working on this with Felix for some time, we don't think this is necessarily the final implementation - there are a lot of unknowns and new @todos - but it does decouple SDCs from Components, introduce the capability of placing at least some Blocks, and allows some other issues to be worked on in parallel.
Comment #23
longwaveComment #24
longwaveComment #25
longwaveOpened several followups for @todos tagged here.
#3484671: Add support for block derivatives
#3484672: Display disabled Block components in /admin/structure/component/status
#3484673: Remove ComponentSourceInterface::getDependencies() and Component::calculateDependencies()
#3484678: Simplify ComponentSourceInterface::getClientSideInfo(), introduce ClientSideRepresentation value object, and leverage it
#3484682: Handle update and delete of Block `Component`s, plus react to dependency removal
Also two followups to discuss handling block settings:
#3484666: Show configuration forms for Block components
#3484669: Define how block settings should be presented in the UI
Comment #26
longwaveEven though we have two source plugins now it's still hard to be 100% confident that we have fully decoupled things in the right way. I feel like it would be helpful to have a third source plugin to really solidify the abstractions here and make sure we have done the right thing. But I don't think that third plugin is known yet so this will have to do for now, we are still in the early phases anyway and landing this means the followups and numerous other issues can be worked on in parallel.
Comment #27
wim leers💯
We do not have designs/UX for all XB component types. The designs do not cover the unknown territory of "abstract additional component types", and hence we cannot possibly know what is shared (across component types) vs unique (per component type).
There's no way around this IMHO, but to just build what we do know and acknowledge it will need to evolve.
Well, we do know that we'll need to support layout plugins for sure. See #3454519: [META] Support component types other than SDC, block, and code components.
Comment #28
tedbowI think this good enough considering the number of follow-ups we have. There are number of places we assume "props" but I think handling that in #3484666: Show configuration forms for Block components is good enough to get things moving
Comment #29
tedbowLooks good! Thanks everyone!
Comment #30
wim leersGiven this has not been merged yet and sitting at RTBC for 2 hours, I'd like to do one final review pass because it's such a critical leap forward.
Reviewing in the next few hours. If I fail to post my review in the next 3.5 hours, go ahead and merge 🤓
Comment #31
wim leersFinished doing a thorough review. 🥵
This looks great compared to when I last reviewed this >10 days ago! 👍
3 things stand out to me:
'settings_tray' => FALSE,criterion is AFAICT at odds with what thePageTemplatefunctionality (introduced in #3478537: Introduce an XB `PageTemplate` config entity) must able to provide. See https://git.drupalcode.org/project/experience_builder/-/merge_requests/3... for details.plugin_idkey-value pair in its settings seems wrong. Details: https://git.drupalcode.org/project/experience_builder/-/merge_requests/3.... I think we can do better. I'll push a commit for that tomorrow.propsterm that SDC uses and XB adopted too … kinda gets in the way when abstracting to multiple component types. I proposed "explicit inputs" (as the generalized version of "SDC props" and "block plugin settings") and "implicit inputs" (as the generalized version of "block contexts"; SDCs have no such concept). See #3484666-5: Show configuration forms for Block components for more detail.… plus a bunch of things lacking clarity, some of which @longwave already addressed.
I think only the first point is commit-blocking, plus the simple requests for clarification on the MR beyond those 3 big points.
Comment #32
longwaveComment #33
wim leersWhile reviewing the cacheability test coverage remark I posted in detail, I discovered that there was an actual performance (cacheability) regression. Details at https://git.drupalcode.org/project/experience_builder/-/merge_requests/3.... Fixed.
I'll deal with #31.2 WRT plugin ID handling in a follow-up — because I'm not entirely sure yet my idea will work.
Comment #35
wim leersSee y'all in the follow-ups!
Finally, about this bit of the issue summary:
→ the infrastructure for that is being added in #3482393: HTTP API: update /xb-components to specify each `Component`'s `source`.
Comment #36
el7cosmosApologize for coming late, I had some time looking at this.
When I disable a block component via
/admin/structure/component, I can't enable it back again with an error message.
Is this something to worry about? Or should this be a follow-up issue?
Comment #37
el7cosmosOops sorry, accidentally changed status
Comment #38
wim leers@el7cosmos I think that's in scope for #3484672: Display disabled Block components in /admin/structure/component/status. Will repeat #36 there 👍😊
Comment #39
wim leersFYI: In descoping things from this issue/MR, one crucial data integrity aspect for the overall XB architecture was also descoped; I started pushing that forward in #3485917: `ComponentTreeMeetRequirements` constraint validator must be updated now that Blocks-as-Components are supported + make PageTemplate render main content!.
Comment #40
tedbowlooks like 0.x is now failing since this commit https://git.drupalcode.org/project/experience_builder/-/pipelines?page=1...
I can't get it to fail locally
So setting to needs work to alert anyone who is following this issue and might have ideas. We should probably fix in another issue
It could have been a drupal core change but I have pull the latest changes and can't get it to fail
Comment #41
tedbowCreated #3486168: Test failing on 0.x
Comment #42
wim leersFYI: follow-up #3484672: Display disabled Block components in /admin/structure/component/status was merged into #3473275: Support disabled Block Components + multiple reasons for incompatibility.