37
loading...
This website collects cookies to deliver better user experience
{
"type": "Container",
"data": {
"id": "4400936b-6158-4943-9dc8-a04c57e1af46",
"items": [
{
"type": "Card",
"data": {
"id": "26b3f355-2f65-4aae-b9fd-609779f24fdd",
"title": "A card example",
"subtitle": "A subtitle",
"items": [
{
"type": "Button",
"data": {
"id": "4400936b-6158-4943-9dc8-a04c57e1af46",
"title": "Button text",
"className": "btn-primary",
"action": {
"type": "call",
"url": "https://pokeapi.co/api/v2/"
},
}
},
]
}
},
{
"type": "Divider",
"data": {
"id": "4400936b-6158-4943-9dc8-a04c57e1af46",
"marginX": 3,
}
},
{
"type": "Card",
"data": {
"id": "4400936b-6158-4943-9dc8-a04c57e1af46",
"title" : "Title",
"headline" : "Month ## - Month ##, ####",
"copy": "A really long text....",
"image" : {
"url" : "https://i.stack.imgur.com/y9DpT.jpg"
},
}
},
{
"type": "Container",
"data": {
"id": "d76e3a5f-01ad-46f6-a45d-3ad9699ecf99",
"fluid": true,
"embeddedView": {
"type": "Input",
"data": {
"id": "26b3f355-2f65-4aae-b9fd-609779f24fdd",
"label": "Input",
"type": "password",
"placeholder": "Password",
"isRequired": false,
"minCharactersAllowed": 1,
"maxCharactersAllowed": 100,
"validations": [
{
"regexType": "eightOrMoreCharacters",
"regexErrorCopy": "Use 8 or more characters"
},
]
}
}
}
}
]
}
}
createElement
. If you know JSX, and how it transpile to regular JS you might heard about this method, basically the function returns a new React element, it works this way:React.createElement(
type,
[props],
[...children]
)
React.createElement
every time we encounter a component that is valid, while this can be done with multiple approaches, we decided to use recursion to achieve it. This was written in typescript and the first step to make things work is to define some custom types/interfaces.// dynamic-rendering.interfaces.ts
// Here is a type to map all the components names.
type ComponentList =
| 'Button'
| 'Card'
| 'Container'
| 'Divider'
| 'Input';
export interface IComponent {
type: ComponentList;
data: {
id: string;
embeddedView?: IComponent;
items?: Array<IComponent>;
[key: string]: unknown;
};
}
// dynamic-rendering.constants.ts
// All the component imports
export const Components = {
Button,
Card,
Container,
Divider,
Input,
};
// dynamic-rendering.service.ts
import React from 'react';
import { IComponent } from './dynamic-rendering.interfaces';
import { Components } from './dynamic-rendering.constants';
export function createPage(data?: IComponent): React.ReactNode {
// Don't render anything if the payload is falsey.
if (!data) return null;
function createComponent(item: IComponent): React.ReactNode {
const { data, type } = item;
const { items, embeddedView, id, ...rest } = data;
return React.createElement(
// TODO: This can be improved
Components[type] as any,
{
// Pass all the props coming from the data object.
...rest,
id,
// Make each react key unique
key: id,
} as any,
// Map if there are items, if not try to render the embedded view as children
Array.isArray(items)
? items.map(renderer)
: renderer(embeddedView ?? null),
);
}
// Don't render anything if the payload is falsey.
function renderer(
config: IComponent | null,
): React.ReactNode {
if (!config) return null;
return createComponent(config);
}
return renderer(data);
}