Overview
Blocked on #3489302: Preview entire page not just content area.
As well as being able to edit a node layout which lives inside the "main content" block, we also need to be able to place components in other theme regions in Experience Builder.
Proposed resolution
Simplistically speaking:
- Extend the layout tree and model
storageclient-side data model to cover the entire page including all regions (i.e. span thePageTemplateconfig entity's per-region component trees and the edited content entity's component tree). - Handle the separation between the two in the backend.
- Let the frontend consider them as a single tree and set of components, with additions to identify regions.
Refinements including technical challenges + nuances to the above simplistic goal: see #16 + #17.
Specifically, the reason to go with the above architecture is this:
[…] minimize both round trips/latency and ensure a single update to the preview that immediately reflects changes across all those entities.
User interface changes
Issue fork experience_builder-3489899
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 #3
longwaveComment #4
wim leersThis MR builds on top of #3489302: Preview entire page not just content area, so let's land that ASAP to make this MR (much) easier to review.
I did review the ADR already though!
Comment #5
longwaveI've added the concept of a
regionnodeType in the layout tree sent from the backend and added some basic support for this to the frontend, hopefully the frontend team can start to work on this now, I don't foresee any major changes to the actual structure from here in order to complete the implementation of this, but there is still quite a way to go on both backend and frontend.Comment #6
wim leersQuoting the IS:
🤔
Is this intended to be temporary or permanent?
Most Content Creators will not be able to modify the "global regions" (so: component trees in the theme's
PageTemplateconfig entity), typically only Site Builders will be allowed to do that.Or is that considered an afterthought for now, because we do not have the concept of "locking" a component (sub)tree yet, which we'll also need for #3455629: [PP-1] [META] 7. Content Templates — aka "default layouts" — affects the tree+props data model?
EDIT: I see I wrote a related comment on the MR 2 days ago: https://git.drupalcode.org/project/experience_builder/-/merge_requests/4...
Comment #7
longwaveThe API should only be sending things that are allowed to be modified. So for the Content Creator they would only get the content entity tree (whether the root of this is a single region, or just an array of components, is still to be determined), but for the Site Builder they would get the full PageTemplate and all regions, with the content entity embedded somewhere inside one of those regions. The frontend should not need to know about permissions or roles, it just allows the user to edit whatever is sent to it.
Comment #8
longwaveOne thing that I am still not clear on is how to delineate the point where the PageTemplate region ends and the content entity begins.
In Drupal the "content" region generally contains the "main content" block which is where the content entity is rendered. It is possible to place other blocks in the same region above or below the "main content" block.
I assume we want to continue this concept in XB? We already have the data model restriction that a PageTemplate must contain a "main content" block, so it feels like we should continue this in this data model. The "content" region should therefore contain a "block.system_main_block" component. Unlike other block components, we could force this specific component to have a single slot, which is where the content entity tree lives?
There is still a problem in that we need to limit the front end here further than we already have; the user needs to be prevented from moving components out of the "main content block" slot and into another part of the "content" region. But also, how do we explain the difference to the user of a component being inside the content entity/"main content block" and being in the "content" region but outside of the "main content block"?
We could try to sidestep this for now by only placing the "main content block" in the "content" region (perhaps along with the messages block), as this feels like a bit of an edge case; it's easy to add e.g. a "content top" region to a theme instead.
Comment #9
wim leers#7: WFM! 👍
#8:
Hah, I just finished a whole write-up about that over at #3489302-28: Preview entire page not just content area, because @lauriii pointed out the preview was still imperfect. The reason for that is closely connected to what you just described here.
Details:
Let's start with that, but eventually, the main content block should be placeable in any region of the theme. But that depends on the UX for editing just the
PageTemplate(independent of any concrete route) being defined and designed. For now your proposal is pragmatic and fine 👍Right!
That is exactly where we need the designs to provide clear affordances that make the user aware what they're editing: I think that by default the global regions should NOT be editable while editing e.g.
PageorNode7. Otherwise the Content Creator might unwittingly be modifying thePageTemplateand hence affect all routes/HTML responses on the site, not just the one forPageorNode7.I also touched upon that need in #3489302-28: Preview entire page not just content area.
Comment #10
wim leersThat last blockquote in #9 is actually why I'm concerned about this bit in the IS:
… because that literally blurs the lines between "the component tree in the sole content entity being edited" and "the component trees in the theme's
PageTemplatethat literally affect the entire site".And this is AFAICT wrong? @lauriii himself said the same thing over at #3489302-26: Preview entire page not just content area, because the pieces he identified as missing in those screenshots aren't rendered by an XB component tree.
Comment #11
lauriiiThe global regions should not be editable by default when editing a page or node. The interaction that design defined for this is that users need to double click a global region, which opens up the region in a "focus mode" where user is now allowed to make changes to the global region. How this looks visually has been defined here: https://www.figma.com/design/Ps3m4APGHIILsBrm0Uj31N/Experience-Builder?n....
I'm not convinced we would need to support this. This is easy enough to workaround by providing a "Above content" and "Below content" regions so I'm not convinced that we should convolute the UX to add support for this use case.
If we need this, this seems like a setting for the theme. This doesn't seem something that would have to be editable in XB. I'm not aware of common use cases that would require moving the main content block to another region after the initial theme setup.
Comment #12
wim leersOh wow, you're saying that in the XB-powered world, a theme would actually define in its metadata which region should contain the main content block (
Drupal\Core\Block\MainContentBlockPluginInterface), and it should then contain nothing else? If so, we'll need to adjust the validation constraints for thePageTemplateconfig entity.If so: do we want the same treatment for the page title (
Drupal\Core\Block\TitleBlockPluginInterface) and messages (Drupal\Core\Block\MessagesBlockPluginInterface)?That's crucial information not present in the that potentially would've made #3478537: Introduce an XB `PageTemplate` config entity simpler… 😅
Comment #14
longwave@wim leers BlockPageVariant already has this fallback:
I think for a first iteration in XB it is fine to assume the same thing, ie. that the "content" region contains the messages block and main content block in that order - and in the layout/model then we structure it so the user cannot change this for now then that simplifies things; the "content" region to the UI only needs to contain the components for the content entity.
Page title block should probably still be placeable by the user - and in fact there seems no hard requirement that it *must* be on a page, it's not critical?
Comment #15
lauriii99% of the time sites would display these foundational blocks in a region and there wouldn't be any complexity involved with it. However, there are some use cases where context is used so that the block could be displayed in a different region depending on the page.
The approach that I think would work here is to allow theme to decide a placement of a block. Once theme has decided on it's placement, it would not be available to be used in XB. This would particularly well for
Drupal\Core\Block\MessagesBlockPluginInterfaceandDrupal\Core\Block\MainContentBlockPluginInterface. Maybe it doesn't even have to be generic since it's quite specific to these two blocks.The outlier here is
Drupal\Core\Block\TitleBlockPluginInterfacebecause in most scenarios, it would not make sense to display the title block for Pages and Nodes because the title would be defined either in content or the content type template. However, other controllers may need the title to be displayed. If we implemented the generic way for themes to place blocks, we could potentially fit the title use case there too without introducing conditionality for the placement by allowing the template to not render the variable when the title is not desired. Otherwise I guess for now we'd just allow users to place the title in case that it's something they'd need.This is all really fascinating and brings me flashbacks from #2476947: Convert "title" page element into a block.
Comment #16
jessebaker commentedFollowing a meeting between @wim leers, @longwave, @f.mazeikis and I, I created the following notes:
Comment #18
wim leers@longwave in #14
Aha, so the
contentregion becomes (very) special-cased! That can work. Note that thePageTemplateconfig entity's validation constraints require those 3 special blocks to be present, so rather than doing that fallback logic inPageTemplateDisplayVariant::build()(similar to the code you quoted fromBlockPageVariant::build()), we can and IMHO should do it inPageTemplate::create(). (There's precedent for that: see implementations ofEntityInterface::create()).(Also, I forgot about the
contentregion being assumed by Drupal core to exist, even though themes could not have such a region. It's reasonable, pragmatic, and not in anyway a BC break to assume that region, so: yay! 😄)@lauriii in #15
contentregion.For accessibility and consistency reasons (and others), it's important that the title (of a controller with some logic or of a content entity) is always rendered in the same place, using the same markup.
That's why I doubt the content entity's title can realistically become part of the content entity (template)'s component tree? 🤔
Look at
/admin/structure/types/manage/article/displayin the Standard install profile: the article node's title field does NOT appear there! It also does not appear incore.entity_view_display.node.article.default.ymlat all. This is due to:not also doing
(see the code in
\Drupal\field_ui\Form\EntityDisplayFormBase::getFieldDefinitions(): it filters away any field definition that is not "display-configurable").@jessebaker in #16
Thank you so much for summarizing our conversation! 🤩👏
RE: 1+2 → yep, that's what @longwave explained in #14 and I now see can work.
RE: 4 → this implies that a single request from the client might modify both the content entity currently visible in the XB UI and the active theme's
PageTemplateconfig entity simultaneously! We discussed that in some detail, and @longwave believes this this is viable, although he agrees this is entirely new ground in all of Drupal (saving a content + config entity together in a single transaction), especially given that we'll have to save them into tempstore/a workspace and then publish it all together.⚠️ This does imply that the
wse_configcontrib module will become a hard dependency for XB!💁♂️ Alternatively we could've gone with the config overrides PoC I did back in April, but that was just for verifying we wouldn't need Workspaces to achieve the intended UX. I think since then, the design has evolved sufficiently, as well as technical discussions, to have concluded that XB will rely on Workspaces. I think it'd be good for @lauriii to formally document/state that though :)
RE: 5 → @tedbow and I got the basics of that "transform validation errors' server-side property paths to client-side request body equivalents" logic working in #3478565: Add Entity Update controller. So I'm convinced it's possible albeit not trivial. But both @longwave and @jessebaker believe it's important to keep the client A) as simple as possible, B) to prevent out-of-sync-sequential-updates to the preview: by being able to send everything at once from the client to the server (this issue:
PageTemplateconfig entity + content entity, future:PageTemplateconfig entity + content type template config entity aka #3455629: [PP-1] [META] 7. Content Templates — aka "default layouts" — affects the tree+props data model + content entity), we minimize both round trips/latency and ensure a single update to the preview that immediately reflects changes across all those entities.👆 Point 5 above (the bolded bit at the end) is IMHO the strongest reason to go down this path of keeping the client blissfully unaware of any server-side representations, and make the server side much more complex by forcing it to map a single request body onto many entities to update. 👍
Comment #19
wim leers#3489302: Preview entire page not just content area is in, updating title.
Issue summary fully updated now.
Comment #20
wim leersMade @larowlan aware of this direction over at #3467954-31: META: Evolve XB UI's data model to allow non-SDC components' inputs, DynamicPropSource support, etc., to ensure he doesn't go in a conflicting direction.
Comment #21
lauriiiI don't think mandating this as a requirement matches well with real life requirements that sites have from design perspective. I agree that title has some nuance to it which makes it different from messages and the main.
Here are few example where it's easy to see there are many sites where there isn't a lot of consistency on how the title is displayed between pages:
Anything that would impact the live site should go through the temp store. I was originally thinking that maybe adding new sections wouldn't go through the temp store but there could be some interesting edge cases with that because someone might be creating a section using a component that only exists in the temp store. 🤔
Comment #22
longwaveI think the title block issue can be dealt with in a followup, while it is important it's not on the critical path here. If we allow users to place the title block they can use it if they like, but also they could choose not to use it and just embed H1s in their custom pages that they built, so I'm not even sure it's up to us to decide what to do with it (other than not enforce its placement, which we do at the moment).
This is only really due to historical reasons, core has been trying to unpick this for a long time and progress is slow: #2353867: [META] Expose Title and other base fields in Manage Display
Comment #23
longwaveAgreed with @jessebaker that we should open child issues to handle some of the data model changes here instead of trying to do them all in one MR.
Comment #24
wim leers#3491013: Rework layout API to separate components and slots is in!
Let's rebase this :)
Comment #25
larowlanAnother related issue. Going for a rebase here as #3494114: Implement auto-save of the page template config entity will need the changes here
Comment #26
larowlanPushed some work to start building the regions based on the page template. It's failing openapi spec validation at the moment
Will continue tomorrow.
Comment #28
wim leers=
Comment #30
larowlanCrediting Nathan who pointed me at cli viewport size as a possible cause of the failing e2e test when only in headless mode
Comment #31
larowlanCrosspost
Comment #33
longwaveNot sure we need
explicitInputNameboth as a const and in the plugin metadata; we should pick one only.One nit about a missing
t()but otherwise given this is green I think we should land it and continue work in followups.Comment #34
wim leers@longwave in #33: to have it available in the annotation-based plugin definition' but also use that information from the plugin definition in the code. This is nothing new; I'm just matching the existing pattern that you and @f.mazeikis introduced (see
BlockComponent::SOURCE_PLUGIN_ID) in #3475584: Add support for Blocks as Components? 😅Comment #35
wim leersComment #37
wim leersComment #38
wim leers🥳
See y'all in:
(@longwave please link that issue and mark this as when you've done that!)
Post-humously bumped to considering how many issues this unblocked! 😅
Comment #39
longwaveOpened #3494859: Rename Component config ComponentSource plugin-specific configuration keys for clarity for #38.5.