-
-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add MetaMask Template Renderer (#10307)
* add MetaMask Template Renderer * add areEqual fn and change acc var name * use key
- Loading branch information
1 parent
6a89261
commit 96933b3
Showing
4 changed files
with
230 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from './metamask-template-renderer' |
102 changes: 102 additions & 0 deletions
102
ui/app/components/app/metamask-template-renderer/metamask-template-renderer.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import React, { memo } from 'react' | ||
import PropTypes from 'prop-types' | ||
import { isEqual } from 'lodash' | ||
import { safeComponentList } from './safe-component-list' | ||
|
||
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 | ||
} | ||
|
||
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 | ||
const Element = getElement(sections) | ||
return ( | ||
<Element {...sections.props}> | ||
{typeof sections.children === 'object' ? ( | ||
<MetaMaskTemplateRenderer sections={sections.children} /> | ||
) : ( | ||
sections?.children | ||
)} | ||
</Element> | ||
) | ||
} | ||
|
||
// The last case is dealing with an array of objects | ||
return ( | ||
<> | ||
{sections.reduce((allChildren, child) => { | ||
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) | ||
allChildren.push( | ||
<Element key={child.key} {...child.props}> | ||
{child?.children} | ||
</Element>, | ||
) | ||
} | ||
} | ||
return allChildren | ||
}, [])} | ||
</> | ||
) | ||
} | ||
|
||
const SectionShape = { | ||
props: PropTypes.object, | ||
element: PropTypes.oneOf(Object.keys(safeComponentList)).isRequired, | ||
key: PropTypes.string, | ||
} | ||
|
||
const ValidChildren = PropTypes.oneOfType([ | ||
PropTypes.string, | ||
PropTypes.shape(SectionShape), | ||
PropTypes.arrayOf( | ||
PropTypes.oneOfType([PropTypes.shape(SectionShape), PropTypes.string]), | ||
), | ||
]) | ||
|
||
SectionShape.children = ValidChildren | ||
|
||
MetaMaskTemplateRenderer.propTypes = { | ||
sections: ValidChildren, | ||
} | ||
|
||
export default memo(MetaMaskTemplateRenderer, (prevProps, nextProps) => { | ||
return isEqual(prevProps.sections, nextProps.sections) | ||
}) |
106 changes: 106 additions & 0 deletions
106
ui/app/components/app/metamask-template-renderer/metamask-template-renderer.stories.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import React from 'react' | ||
import { object } from '@storybook/addon-knobs' | ||
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system' | ||
import MetaMaskTemplateRenderer from '.' | ||
|
||
export default { | ||
title: 'MetaMask Template Renderer', | ||
} | ||
|
||
const SECTIONS = { | ||
element: 'Box', | ||
props: { | ||
margin: 4, | ||
padding: 8, | ||
borderColor: COLORS.PRIMARY1, | ||
borderWidth: 2, | ||
}, | ||
children: [ | ||
{ | ||
element: 'Typography', | ||
key: 'A Test String', | ||
children: 'A Test String', | ||
props: { | ||
color: COLORS.UI3, | ||
variant: TYPOGRAPHY.H2, | ||
}, | ||
}, | ||
{ | ||
element: 'Typography', | ||
key: 'Some more text', | ||
children: 'Some more text as a paragraph', | ||
props: { | ||
color: COLORS.UI4, | ||
variant: TYPOGRAPHY.Paragraph, | ||
}, | ||
}, | ||
{ | ||
element: 'TruncatedDefinitionList', | ||
key: 'TDL', | ||
props: { | ||
dictionary: { | ||
term: | ||
'a word or phrase used to describe a thing or to express a concept, especially in a particular kind of language or branch of study.', | ||
definition: | ||
'a statement of the exact meaning of a word, especially in a dictionary.', | ||
dl: 'HTML tag denoting a definition list', | ||
dt: 'HTML tag denoting a definition list term', | ||
dd: 'HTML tag denoting a definition list definition', | ||
}, | ||
title: 'Full list', | ||
prefaceKeys: ['term', 'definition'], | ||
}, | ||
}, | ||
{ | ||
element: 'Box', | ||
key: 'ActionsBox', | ||
children: [ | ||
{ | ||
element: 'Button', | ||
children: 'Cancel', | ||
key: 'cancel-button', | ||
props: { | ||
rounded: true, | ||
type: 'outlined', | ||
style: { | ||
width: '45%', | ||
}, | ||
}, | ||
}, | ||
{ | ||
element: 'Button', | ||
children: 'OK', | ||
key: 'ok-button', | ||
props: { | ||
rounded: true, | ||
type: 'primary', | ||
style: { | ||
width: '45%', | ||
}, | ||
}, | ||
}, | ||
], | ||
props: { justifyContent: 'space-between', padding: [0, 4] }, | ||
}, | ||
], | ||
} | ||
export const metaMaskTemplateRenderer = () => ( | ||
<MetaMaskTemplateRenderer sections={object('sections', SECTIONS)} /> | ||
) | ||
|
||
export const withInvalidElement = () => ( | ||
<MetaMaskTemplateRenderer | ||
sections={object('sections', [ | ||
{ | ||
...SECTIONS, | ||
key: 'safe-tree', | ||
}, | ||
{ | ||
element: 'Unsafe', | ||
key: 'unsafe-tree', | ||
children: | ||
'I should be displayed, but I wont be due to unsafe component', | ||
}, | ||
])} | ||
/> | ||
) |
21 changes: 21 additions & 0 deletions
21
ui/app/components/app/metamask-template-renderer/safe-component-list.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import Button from '../../ui/button' | ||
import Chip from '../../ui/chip' | ||
import DefinitionList from '../../ui/definition-list' | ||
import TruncatedDefinitionList from '../../ui/truncated-definition-list' | ||
import Popover from '../../ui/popover' | ||
import Typography from '../../ui/typography' | ||
import Box from '../../ui/box' | ||
|
||
export const safeComponentList = { | ||
b: 'b', | ||
p: 'p', | ||
div: 'div', | ||
span: 'span', | ||
Typography, | ||
Chip, | ||
DefinitionList, | ||
TruncatedDefinitionList, | ||
Button, | ||
Popover, | ||
Box, | ||
} |