Slots
A Slot is a high-level interface for developers to define and manage dynamic content insertion within drop-in components.
Context
The context is defined during implementation of a drop-in and can be used to pass data and functions to the slot.
Pre-built Methods
- dictionary: The dictionary of the selected language.
- replaceWith: A function to replace the slot’s content with a new HTML element.
- appendChild: A function to append a new HTML element to the slot’s content.
- prependChild: A function to prepend a new HTML element to the slot’s content.
- appendSibling: A function to append a new HTML element after the slot’s content.
- prependSibling: A function to prepend a new HTML element before the slot’s content.
- getSlotElement: A function to get a slot element.
- onChange: A function to listen to changes in the slot’s context.
Implementing a new slot
The <Slot />
component is used to define a slot in a container. It receives a name and a slot object with the following properties:
name
The name of the slot in PascalCase. string
(required).
slotTag
The HTML tag to use for the slot’s wrapper element. This allows you to change the wrapper element from the default div
to any valid HTML tag (e.g., ‘span’, ‘p’, ‘a’, etc.). When using specific tags like ‘a’, you can also provide their respective HTML attributes (e.g., ‘href’, ‘target’, etc.).
Example:
// Render with a span wrapper<Slot name="MySlot" slotTag="span"> Inline content</Slot>
// Render with an anchor wrapper<Slot name="MySlot" slotTag="a" href="https://example.com" target="_blank"> Link content</Slot>
contentTag
The HTML tag to use for wrapping dynamically inserted content within the slot. This is separate from the slot’s wrapper tag and allows you to control how dynamic content is structured. Defaults to ‘div’.
Example:
<Slot name="MySlot" slotTag="article" // The outer wrapper will be an article contentTag="section" // Dynamic content will be wrapped in sections slot={(ctx) => { const elem = document.createElement('div'); elem.innerHTML = 'Dynamic content'; ctx.appendChild(elem); // This will be wrapped in a section tag }}/>
slot (required)
ctx
: An object representing the context of the slot, including methods for manipulating the slot’s content.
The slot property, which is implemented as a promise function, provides developers with the flexibility to dynamically generate and manipulate content within slots. However, it’s important to note that this promise is render-blocking, meaning that the component will not render until the promise is resolved.
context
The context property in the Slot component lets developers pass extra information or functionality to customize how the slot behaves or interacts with the application. This information is accessible within the slot’s rendering logic, allowing for tailored slot behavior based on specific needs or application states.
render
The render property in the Slot component lets developers define how the content within the slot should be displayed and should be used when developers need fine-grained control over what content appears within the slot. It’s particularly useful when using custom slot methods (see Privates below) in scenarios where the content to be displayed within the slot is dynamic and may depend on properties passed to another component.
children
The children property in the Slot component represents the content that is passed directly within the opening and closing tags of the Slot component. It allows developers to include static content directly within the slot, which will be rendered as part of the slot’s contents. This property is useful for cases where the content within the slot is static or does not need to be dynamically generated by the slot.
// MyContainer.tsx (Drop-in)
import { HTMLAttributes } from 'preact/compat';import { Container, Slot, SlotProps } from '@adobe-commerce/elsie/lib';
export interface MyContainerProps extends HTMLAttributes<HTMLDivElement> { slots?: { MyOpenSlot?: SlotProps<{ // MyOpenSlot Context data: MyContainerData; }>; };}
export const MyContainer: Container<MyContainerProps> = ({ slots, children, ...props}) => { // ...
return ( <div {...props}> <Slot name="MyOpenSlot" slot={slots?.MyOpenSlot} context={{ data }} /> </div> );};
// blocks/my-block.js (storefront)
provider.render(MyContainer, { slots: { MyOpenSlot: async (ctx) => { // create a new HTML element const element = document.createElement('div'); // set the innerHTML of the new element to the text from the context's data element.innerHTML = ctx.data.text;
// append the new element to the slot's content ctx.appendChild(element);
// ...or you could also use any of the other slot methods to manipulate the slot's content // ctx.replaceWith(element); // ctx.prependChild(element); // ctx.appendSibling(element); // ctx.prependSibling(element);
// to listen and react to changes in the slot's context (lifecycle) ctx.onChange((next) => { // update the innerHTML of the new element to the new text from the context's data element.innerHTML = ctx.data.text; }); }, },});
Privates
The <Slot />
component has a private interface that serves as a mechanism for managing internal complexity and promoting clean, modular design within the Slot component or related components.
_registerMethod
The _registerMethod
private function is used to register a method in the slot’s context which is particularly helpful in scenarios where dynamic behavior or interactions need to be incorporated into the Slot component.
Slot Methods also include the ability to modify the slot’s state or content based on external interactions or changes in application state.
_setProps
The _setProps
private function within the Slot component is responsible for dynamically updating the properties of the slot. It allows developers to modify the slot’s state or content based on external interactions or changes in application state, triggering re-renders of the slot component with updated properties.
_htmlElementToVNode
The _htmlElementToVNode
private function in the Slot component converts HTML elements into virtual DOM nodes (VNodes), enabling their integration into Preact components. This conversion facilitates the dynamic insertion of HTML content into slots while benefiting from Preact’s virtual DOM reconciliation and rendering.
// MyContainer.tsx (Drop-in)
<Slot name="MyOpenSlot" slot={slots?.MyOpenSlot} context={{ // custom slot method appendButton(callback) { // use _registerMethod to register a method in the slot's context this._registerMethod((...attrs) => { // callback return the values provided by the storefront developer const { text, ...buttonProps } = callback(...attrs);
const button = ( <Button type="button" {...buttonProps}> {text} </Button> );
// use _setProps to update the slot's properties this._setProps((prev: any) => ({ children: [...(prev.children || []), button], })); }); }, }} render={(props) => { // render the slot's content using props mutated by the slot's methods return <Buttons>{props.children}</Buttons>; }}/>
// blocks/my-block.js (storefront)
provider.render(MyContainer, { slots: { // Available Slots MyOpenSlot: (ctx) => { ctx.appendButton: (next, state) => { // use state to get the current state of the slot const loading = state.get('loading');
return { text: loading ? 'Loading' : 'Click me!', onClick: async () => { // use state to update the state of the slot state.set('loading', true);
await doSomething().finally(() => { state.set('loading', false); }); }, }; }, }, },});