- Issue created by @penyaskito
- π¬π§United Kingdom jessebaker
When building/designing this "chip" that shows the variant wrapper, consideration must be made for how we intend to show/handle "selecting components that are stacked one inside the other and take up the same space"
It might be possible to address both at the same time if we can get designs that incorporate both things.
As an example of a competitor's solution to the "components taking up the same space" here is a screenshot of Webflow showing the parent's of a selected component:
Site Studio also has a solution that's quite neat (but I think could be improved upon). I can try and find a screenshot of that if it's helpful.
- πΊπΈUnited States effulgentsia
wim leers β credited effulgentsia β .
- π§πͺBelgium wim leers Ghent π§πͺπͺπΊ
@jessebaker: how do you imagine the client will:
- know to render such a chip?
- know what label to use in the chip?
The current thinking by @penyaskito, @effulgentsia and I (per meeting yesterday):
- @penyaskito's proposal can be summarized as this:
- Then adding new variants would add more siblings to
uuid-here-personalization-wrapper-yo/variants
. - To inform the client side about the fact that actually only one of the variants should be visible at the same time in both the preview and in the layers panel, we could allow
nodeType: slot
to contain amutuallyExclusiveChildren: true
flag.(Keeping the same set of
nodeType
s but adding more metadata was proposed by @effulgentsia.)We'd set that on
uuid-here-personalization-wrapper-yo/variants
. It'd indicate that only one of its children is visible at a time.That same flag could then be used in future functionality where different subtrees should be visible in different circumstances, e.g. for Framer-like responsive design (which allows different component trees per breakpoint β not saying I think this is a good idea at all, just that it's something actually real-world-like).
- We'd then also need a flag on
nodeType: component
to either:- let the client side know what the segments are for each child to be visible (can happen entirely client-side upon selecting which segment the XB preview should preview π β at most requiring a round trip for updating the preview, but it's possible for the server to always provide that information and letting the client swap that in!)
i.e.
β¦ // π‘The first variant, by default named "default". "nodeType": "component", "id": "uuid-here-personalization-variant-default", "type": "personalization.variant", // π declarative choice "segments": ["belgium", "uk", "spain"], "slots": [ { "nodeType": "slot", "id": "uuid-here-personalization-variant-default/variant", "components": [ // π‘this is the original! { "nodeType": "component", "id": "baf231e8-b214-4e3e-93d3-5d3f03a1eae9", "type": "sdc.experience_builder.druplicon", "slots": [] } ] } ] β¦
- let the client side know which of those children is currently active (requires talking to the server first π)
i.e.
<code> β¦ // π‘The first variant, by default named "default". "nodeType": "component", "id": "uuid-here-personalization-variant-default", "type": "personalization.variant", // π imperative choice "active": true, "slots": [ { "nodeType": "slot", "id": "uuid-here-personalization-variant-default/variant", "components": [ // π‘this is the original! { "nodeType": "component", "id": "baf231e8-b214-4e3e-93d3-5d3f03a1eae9", "type": "sdc.experience_builder.druplicon", "slots": [] } ] } ] β¦
- let the client side know what the segments are for each child to be visible (can happen entirely client-side upon selecting which segment the XB preview should preview π β at most requiring a round trip for updating the preview, but it's possible for the server to always provide that information and letting the client swap that in!)
So: thoughts? Does it make sense to you to add flags to existing client-side data model
nodeType
s, or do you think it's better to instead introduce newnodeType
s? - π¬π§United Kingdom jessebaker
+1 to using existing components and slots and just adding flags!
+1 to the declarative choice of"segments": ["belgium", "uk", "spain"]
over the imperative"active": true
I really like the "After personalising" example but I do think it can be simplified (perhaps this is the "optimized further" that you allude to).
In this example, instead of both a
personalization-wrapper
and apersonalization-variant
component, there is just apersonalization-wrapper
and it has a"mutuallyExclusiveSlots": true,
flag. Then it is the slots of that component that contain the"segments": ["belgium", "uk", "spain"],
information.... { // π‘Wrapped in a `personalization.wrapper` component, which will contain the different variants. "nodeType": "component", "id": "uuid-here-personalization-wrapper-yo", "type": "personalization.wrapper", "mutuallyExclusiveSlots": true, "slots": [ // π‘The first variant { "nodeType": "slot", "id": "uuid-here-personalization-wrapper-yo/variant/default", "segments": ["belgium", "uk", "spain"], "components": [ // π‘this is the original! { "nodeType": "component", "id": "baf231e8-b214-4e3e-93d3-5d3f03a1eae9", "type": "sdc.experience_builder.druplicon", "slots": [] } ] }, // π‘The second variant { "nodeType": "slot", "id": "uuid-here-personalization-wrapper-yo/variant/2", "segments": ["uk", "finland"], "components": [ // π‘this is the other version { "nodeType": "component", "id": "baf231e8-b214-4e3e-93d3-5d3f03a1eae9", "type": "sdc.experience_builder.xbicon", "slots": [] } ] } ] }
I have spotted a potential scenario, though, that means that perhaps
mutuallyExclusiveSlots
is not the correct name for the flag! In the above example an audience member in the UK might expect to see both variants at the same time and thus those are notmutallyExclusiveSlots
but ratherconditionalSlots
.However, I do think there is a need for the
mutallyExclusiveSlots
and that is when the purpose of the variant is A/B testing. Where a site author wants 50% of their audience to see one variant and the other 50% to see the other. So perhaps both flags are needed? - πΊπΈUnited States effulgentsia
Keeping the same set of nodeTypes but adding more metadata was proposed by @effulgentsia.
+1 to using existing components and slots and just adding flags!
Although that was my original proposal earlier this week, I changed my mind since then, and @wim leers, @penyaskito, and I met earlier today and converged on adding two new nodeTypes:
switch
andcase
to supplement the 3 that XB already has (region
,component
, andslot
) for a total of 5. The reasoning for this is that the concept of "show only one of these depending on some logic" isn't limited to just how the resulting page gets rendered, but rather this switch/case concept needs to be integrated into the whole XB UI:- The layers panel needs to show only one of the variants at a time.
- A chip needs to be added to the overlay for the active variant whenever a descendant is selected.
- Clicking on that chip needs to open an entire React-driven (not Drupal-driven) UI in the right sidebar for configuring all of the variants and the mapping from the personalization segment to the variant.
- From that right sidebar, you can click "Edit" on a variant that's not the one that should be shown to your currently selected audience segment, and doing so switches the canvas to a focus-mode editing experience, similar to editing global regions, where you can only work with that variant's subtree and nothing else that's on the page.
All of that combined, but especially those last two, is a lot of logic that's specific to the concept of "here are multiple subtrees, optimize the UI for working with only one at a time, and managing which one should be active when". Having the React code implementing all of that logic based on
nodeType == 'component' && type == 'personalization.wrapper'
feels like breaking the simplicity of the React app basing its logic on the nodeType and leaving the details of the component "type" to the back-end.The idea of
nodeType == 'component' && mutuallyExclusiveSlots
has some appeal as the "flag" to the React app to do all the "optimize the UI for working with only one subtree at a time" things, but the downside with this is it implies that on the Drupal side, this will be managed as a single component with multiple (dynamic based on how many variants got created in the UI) slots. That might or might not be the desired back-end implementation. In fact, so far we're leaning towards a component-per-variant plus a component-for-the-group approach in order to avoid the concept of dynamic slots. Whether or not we stick with that or end up reconsidering the dynamic slots idea ideally should not require changing the client-side structure to have to match it.And that all leads us to the idea of adding two new nodeTypes to be explicit about the new functionality being added to the UI without being coupled to how that ends up getting stored on the server.
With that, #5 becomes:
"nodeType": "switch", "id": "UUID-of-the-Personalization-Wrapper", "type": "personalization", // π‘ In the future, could add other types, such as perhaps "breakpoints". "mapping: { // π‘ Show nothing to these segments. "us": null, "uk": null, // π‘ The default variant. Shown to all segments not otherwise included in this mapping. "*": "UUID-of-a-Variant", // π‘ Show a different variant to these segments. "finland": "UUID-of-another-Variant", "norway": "UUID-of-another-Variant" }, "cases": [ { "nodeType": "case", "id": "UUID-of-a-Variant", "components": [ // One or more components to show for this variant. ... ] }, { "nodeType": "case", "id": "UUID-of-another-Variant", "components": [ // One or more components to show for this variant. ... ] } ] }
I have spotted a potential scenario, though, that means that perhaps mutuallyExclusiveSlots is not the correct name for the flag! In the above example an audience member in the UK might expect to see both variants at the same time and thus those are not mutallyExclusiveSlots but rather conditionalSlots.
We clarified with @lauriii that this isn't what's desired in terms of design. Any given segment should only map to one variant (or to None), but multiple segments can map to the same variant. That's why in the above I moved from
segments
on the variant/case nodes tomapping
on the wrapper/switch node.However, I do think there is a need for the mutallyExclusiveSlots and that is when the purpose of the variant is A/B testing. Where a site author wants 50% of their audience to see one variant and the other 50% to see the other. So perhaps both flags are needed?
A/B testing is out of scope for the initial version of this. But in the future, we can expand
mapping
to accommodate it. There's probably multiple options for how to model that, but just as one naive example:"mapping: { "us": null, "uk": null, "*": "UUID-of-a-Variant", "finland": "UUID-of-another-Variant", "norway": { "UUID-of-a-Variant": 0.5, "UUID-of-another-Variant": 0.5 } },
- π§πͺBelgium wim leers Ghent π§πͺπͺπΊ
#6 Thanks so much for the detailed write-up! π Much appreciated π
Then it is the slots of that component that contain
β @jessebaker in #5
+
this will be managed as a single component with multiple (dynamic based on how many variants got created in the UI) slots. [β¦] we're leaning towards a component-per-variant plus a component-for-the-group approach in order to avoid the concept of dynamic slots
β @effulgentsia in #6
Indeed: not multiple dynamic slots, but multiple component instances (one per variant). Each of those component instances itself has a single slot, which contains the component tree for that variant. This achieves the same, without the need for
- making
ComponentSourceWithSlotsInterface
more complicated - complicating the server-side rendering logic ("available slots of a component depends on logic in >=1 explicit inputs")
- complicating the client, because if we did B, the client would also need to be adjusted for that β including additional network I/O and hence latency
Whether or not we stick with that or end up reconsidering the dynamic slots idea ideally should not require changing the client-side structure to have to match it.
π― β yay for
docs/adr/0005-Keep-the-front-end-simple.md
π - making