You can add components as props to work them into the layout of another component. Here is an example from the project.
gravity/frontend/components/blocks/promotions/PromotionsBlock.tsx
{/* BACKEND RENDER */}
{isEditing && (
<div className="flex flex-wrap mb-12">
{promotionsList?.map((promotion) => (
...
))}
{editorControls}
</div>
)}
gravity/backend/plugins/gravity-platform-core/src/blocks/promotions/edit.tsx
editorControls={
<>
{availablePromotions?.length > 0 && (
<Listbox value={selected} onChange={setSelected}>
<div className="relative mt-5 lg:w-1/4">
<Listbox.Button className="relative w-full py-2 pl-3 pr-10 text-left bg-white rounded-lg shadow-md cursor-default focus:outline-none focus-visible:ring-2 focus-visible:ring-opacity-75 focus-visible:ring-white focus-visible:ring-offset-orange-300 focus-visible:ring-offset-2 focus-visible:border-indigo-500 sm:text-sm">
<span className="block truncate">Add Promotion</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<HiOutlinePlusCircle
className="w-5 h-5 text-gray-400"
aria-hidden="true"
/>
</span>
</Listbox.Button>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute w-full py-1 mt-1 overflow-auto text-base bg-white rounded-md shadow-lg max-h-60 ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{availablePromotions?.map((promo) => (
<Listbox.Option
key={promo.id}
className={({ active }) =>
`${
active
? "text-amber-900 bg-amber-100"
: "text-gray-900"
}
cursor-default select-none relative py-2 pl-3 pr-4`
}
value={promo.id}
>
{({ selected }) => (
<>
<span
className={`${
selected ? "font-medium" : "font-bold"
} block truncate`}
>
{promo.title.rendered}
</span>
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</Listbox>
)}
</>
}
We can pass this whole component and it's render logic as a prop. This allows us to fit it into the layout of the display component.
Converting Ids to Actual Objects
frontend/components/blocks/promotions/PromotionsBlock.tsx
const promotionsList = (
selectedPromotionIds?.length
? selectedPromotionIds
?.map((id) =>
promotions.find((promotion) => promotion.databaseId === id)
)
.filter((p) => p)
: promotions
) as IPromotion[];
Here we map the IDs to the objects they represent. The final filter is to be sure that none of those are null.