forked from MetaMask/metamask-extension
-
Notifications
You must be signed in to change notification settings - Fork 0
/
metamask-template-renderer.js
110 lines (103 loc) · 3.44 KB
/
metamask-template-renderer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import React, { memo } from 'react';
import { isEqual } from 'lodash';
import { safeComponentList } from './safe-component-list';
import { ValidChildren } from './section-shape';
function getElement(section) {
const { element } = section;
const Element = safeComponentList[element];
if (!Element) {
throw new Error(
`${element} is not in the safe component list for MetaMask template renderer`,
);
}
return Element;
}
function renderElement(element) {
const Element = getElement(element);
const propsAsComponents = element.propComponents
? getPropComponents(element.propComponents)
: {};
return (
<Element {...element.props} {...propsAsComponents}>
{typeof element.children === 'object' ? (
<MetaMaskTemplateRenderer sections={element.children} />
) : (
element?.children
)}
</Element>
);
}
function getPropComponents(components) {
return Object.entries(components).reduce((accumulator, [key, component]) => {
if (component) {
accumulator[key] = Array.isArray(component)
? component.map(renderElement)
: renderElement(component);
}
return accumulator;
}, {});
}
const MetaMaskTemplateRenderer = ({ sections }) => {
if (!sections) {
// If sections is null eject early by returning null
return null;
} else if (typeof sections === 'string') {
// React can render strings directly, so return the string
return sections;
} else if (
sections &&
typeof sections === 'object' &&
!Array.isArray(sections)
) {
// If dealing with a single entry, then render a single object without key
return renderElement(sections);
}
// The last case is dealing with an array of objects
return (
<>
{sections.reduce((allChildren, child) => {
if (child === undefined || child?.hide === true) {
return allChildren;
}
if (typeof child === 'string') {
// React can render strings directly, so push them into the accumulator
allChildren.push(child);
} else {
// If the entry in array is not a string, then it must be a Section.
// Sections are handled by the main function, but must
// be provided a key when a part of an array.
if (!child.key) {
throw new Error(
'When using array syntax in MetaMask Template Language, you must specify a key for each child of the array',
);
}
if (typeof child?.children === 'object') {
// If this child has its own children, check if children is an
// object, and in that case use recursion to render.
allChildren.push(
<MetaMaskTemplateRenderer sections={child} key={child.key} />,
);
} else {
// Otherwise render the element.
const Element = getElement(child);
const propsAsComponents = child.propComponents
? getPropComponents(child.propComponents)
: {};
allChildren.push(
<Element key={child.key} {...child.props} {...propsAsComponents}>
{child?.children}
</Element>,
);
}
}
return allChildren;
}, [])}
</>
);
};
MetaMaskTemplateRenderer.propTypes = {
sections: ValidChildren,
};
export default memo(MetaMaskTemplateRenderer, (prevProps, nextProps) => {
return isEqual(prevProps.sections, nextProps.sections);
});