mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Extract Menu
component from ConnectedAccountsListOptions
(#8632)
A `Menu` component has been extracted from the `ConnectedAccountsListOptions` component. The menu and the menu items are now the `Menu` and `MenuItem` components respectively. A custom body was added to the Storybook preview to ensure that the `popover-content` element was present in the DOM before the Menu was constructed.
This commit is contained in:
parent
91953f062b
commit
f886686db2
2
.storybook/preview-body.html
Normal file
2
.storybook/preview-body.html
Normal file
@ -0,0 +1,2 @@
|
||||
<div id="custom-root"></div>
|
||||
<div id="popover-content"></div>
|
@ -1,26 +0,0 @@
|
||||
import classnames from 'classnames'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { PureComponent } from 'react'
|
||||
|
||||
export default class ConnectedAccountsListOptionsItem extends PureComponent {
|
||||
static propTypes = {
|
||||
iconClassNames: PropTypes.string.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
onClick: PropTypes.func,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
onClick: undefined,
|
||||
}
|
||||
|
||||
render () {
|
||||
const { children, iconClassNames, onClick } = this.props
|
||||
|
||||
return (
|
||||
<button className="connected-accounts-options__row" onClick={onClick}>
|
||||
<i className={classnames('connected-accounts-options__row-icon', iconClassNames)} />
|
||||
<span>{children}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from './connected-accounts-list-options-item.component'
|
@ -1,34 +1,23 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { useRef, useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { usePopper } from 'react-popper'
|
||||
import React, { useState } from 'react'
|
||||
import { Menu } from '../../../ui/menu'
|
||||
|
||||
const ConnectedAccountsListOptions = ({ children, onShowOptions, onHideOptions, show }) => {
|
||||
const [optionsButtonElement, setOptionsButtonElement] = useState(null)
|
||||
const [popperElement, setPopperElement] = useState(null)
|
||||
const popoverContainerElement = useRef(document.getElementById('popover-content'))
|
||||
|
||||
const { attributes, styles } = usePopper(optionsButtonElement, popperElement, {
|
||||
modifiers: [{ name: 'preventOverflow', options: { altBoundary: true } }],
|
||||
})
|
||||
return (
|
||||
<>
|
||||
<button className="fas fa-ellipsis-v connected-accounts-options__button" onClick={onShowOptions} ref={setOptionsButtonElement} />
|
||||
{
|
||||
show
|
||||
? createPortal(
|
||||
<>
|
||||
<div className="connected-accounts-options__background" onClick={onHideOptions} />
|
||||
<div
|
||||
className="connected-accounts-options"
|
||||
ref={setPopperElement}
|
||||
style={styles.popper}
|
||||
{...attributes.popper}
|
||||
>
|
||||
{ children }
|
||||
</div>
|
||||
</>,
|
||||
popoverContainerElement.current
|
||||
? (
|
||||
<Menu
|
||||
anchorElement={optionsButtonElement}
|
||||
onHide={onHideOptions}
|
||||
popperOptions={{ modifiers: [{ name: 'preventOverflow', options: { altBoundary: true } }] }}
|
||||
>
|
||||
{ children }
|
||||
</Menu>
|
||||
)
|
||||
: null
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import React, { PureComponent } from 'react'
|
||||
import ConnectedAccountsListPermissions from './connected-accounts-list-permissions'
|
||||
import ConnectedAccountsListItem from './connected-accounts-list-item'
|
||||
import ConnectedAccountsListOptions from './connected-accounts-list-options'
|
||||
import ConnectedAccountsListOptionsItem from './connected-accounts-list-options-item'
|
||||
import { MenuItem } from '../../ui/menu'
|
||||
|
||||
export default class ConnectedAccountsList extends PureComponent {
|
||||
static contextTypes = {
|
||||
@ -112,20 +112,20 @@ export default class ConnectedAccountsList extends PureComponent {
|
||||
>
|
||||
{
|
||||
address === selectedAddress ? null : (
|
||||
<ConnectedAccountsListOptionsItem
|
||||
iconClassNames="fas fa-random"
|
||||
<MenuItem
|
||||
iconClassName="fas fa-random"
|
||||
onClick={this.switchAccount}
|
||||
>
|
||||
{t('switchToThisAccount')}
|
||||
</ConnectedAccountsListOptionsItem>
|
||||
</MenuItem>
|
||||
)
|
||||
}
|
||||
<ConnectedAccountsListOptionsItem
|
||||
iconClassNames="fas fa-ban"
|
||||
<MenuItem
|
||||
iconClassName="fas fa-ban"
|
||||
onClick={this.disconnectAccount}
|
||||
>
|
||||
{t('disconnectThisAccount')}
|
||||
</ConnectedAccountsListOptionsItem>
|
||||
</MenuItem>
|
||||
</ConnectedAccountsListOptions>
|
||||
)}
|
||||
/>
|
||||
|
@ -63,55 +63,11 @@
|
||||
}
|
||||
|
||||
.connected-accounts-options {
|
||||
background: $white;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.214);
|
||||
border-radius: 8px;
|
||||
width: 225px;
|
||||
color: $Black-100;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0 16px;
|
||||
right: 24px;
|
||||
|
||||
font-family: Roboto, 'sans-serif';
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
line-height: 20px;
|
||||
|
||||
z-index: 1050;
|
||||
|
||||
&__row {
|
||||
background: none;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 14px 0;
|
||||
cursor: pointer;
|
||||
|
||||
&-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&__button {
|
||||
background: inherit;
|
||||
color: $Grey-500;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
&__background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 1050;
|
||||
}
|
||||
}
|
||||
|
||||
.connected-accounts-permissions {
|
||||
|
@ -30,6 +30,8 @@
|
||||
|
||||
@import '../ui/identicon/index';
|
||||
|
||||
@import '../ui/menu/menu';
|
||||
|
||||
@import 'info-box/index';
|
||||
|
||||
@import 'menu-bar/index';
|
||||
|
2
ui/app/components/ui/menu/index.js
Normal file
2
ui/app/components/ui/menu/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as Menu } from './menu'
|
||||
export { default as MenuItem } from './menu-item'
|
31
ui/app/components/ui/menu/menu-item.js
Normal file
31
ui/app/components/ui/menu/menu-item.js
Normal file
@ -0,0 +1,31 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const MenuItem = ({ children, className, iconClassName, onClick }) => (
|
||||
<button className={classnames('menu-item', className)} onClick={onClick}>
|
||||
{
|
||||
iconClassName
|
||||
? (
|
||||
<i className={classnames('menu-item__icon', iconClassName)} />
|
||||
)
|
||||
: null
|
||||
}
|
||||
<span>{children}</span>
|
||||
</button>
|
||||
)
|
||||
|
||||
MenuItem.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
className: PropTypes.string,
|
||||
iconClassName: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
}
|
||||
|
||||
MenuItem.defaultProps = {
|
||||
className: undefined,
|
||||
iconClassName: undefined,
|
||||
onClick: undefined,
|
||||
}
|
||||
|
||||
export default MenuItem
|
43
ui/app/components/ui/menu/menu.js
Normal file
43
ui/app/components/ui/menu/menu.js
Normal file
@ -0,0 +1,43 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { useRef, useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { usePopper } from 'react-popper'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const Menu = ({ anchorElement, children, className, onHide, popperOptions }) => {
|
||||
const [popperElement, setPopperElement] = useState(null)
|
||||
const popoverContainerElement = useRef(document.getElementById('popover-content'))
|
||||
|
||||
const { attributes, styles } = usePopper(anchorElement, popperElement, popperOptions)
|
||||
|
||||
return createPortal(
|
||||
<>
|
||||
<div className="menu__background" onClick={onHide} />
|
||||
<div
|
||||
className={classnames('menu__container', className)}
|
||||
ref={setPopperElement}
|
||||
style={styles.popper}
|
||||
{...attributes.popper}
|
||||
>
|
||||
{ children }
|
||||
</div>
|
||||
</>,
|
||||
popoverContainerElement.current
|
||||
)
|
||||
}
|
||||
|
||||
Menu.propTypes = {
|
||||
anchorElement: PropTypes.instanceOf(window.Element),
|
||||
children: PropTypes.node.isRequired,
|
||||
className: PropTypes.string,
|
||||
onHide: PropTypes.func.isRequired,
|
||||
popperOptions: PropTypes.object,
|
||||
}
|
||||
|
||||
Menu.defaultProps = {
|
||||
anchorElement: undefined,
|
||||
className: undefined,
|
||||
popperOptions: undefined,
|
||||
}
|
||||
|
||||
export default Menu
|
46
ui/app/components/ui/menu/menu.scss
Normal file
46
ui/app/components/ui/menu/menu.scss
Normal file
@ -0,0 +1,46 @@
|
||||
.menu {
|
||||
&__container {
|
||||
background: $white;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.214);
|
||||
border-radius: 8px;
|
||||
width: 225px;
|
||||
color: $Black-100;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0 16px;
|
||||
|
||||
font-family: Roboto, 'sans-serif';
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
line-height: 20px;
|
||||
|
||||
z-index: 1050;
|
||||
}
|
||||
|
||||
&__background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 1050;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
background: none;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 14px 0;
|
||||
cursor: pointer;
|
||||
|
||||
&__icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
36
ui/app/components/ui/menu/menu.stories.js
Normal file
36
ui/app/components/ui/menu/menu.stories.js
Normal file
@ -0,0 +1,36 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Menu, MenuItem } from '.'
|
||||
import { action } from '@storybook/addon-actions'
|
||||
|
||||
export default {
|
||||
title: 'Menu',
|
||||
}
|
||||
|
||||
export const basic = () => {
|
||||
return (
|
||||
<Menu
|
||||
onHide={action('Hide')}
|
||||
>
|
||||
<MenuItem iconClassName="fas fa-bullseye" onClick={action('Menu Item 1')}>Menu Item 1</MenuItem>
|
||||
<MenuItem onClick={action('Menu Item 2')}>Menu Item 2</MenuItem>
|
||||
<MenuItem iconClassName="fas fa-bold" onClick={action('Menu Item 3')}>Menu Item 3</MenuItem>
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
|
||||
export const anchored = () => {
|
||||
const [anchorElement, setAnchorElement] = useState(null)
|
||||
return (
|
||||
<>
|
||||
<button ref={setAnchorElement}>Menu</button>
|
||||
<Menu
|
||||
anchorElement={anchorElement}
|
||||
onHide={action('Hide')}
|
||||
>
|
||||
<MenuItem iconClassName="fas fa-bullseye" onClick={action('Menu Item 1')}>Menu Item 1</MenuItem>
|
||||
<MenuItem onClick={action('Menu Item 2')}>Menu Item 2</MenuItem>
|
||||
<MenuItem iconClassName="fas fa-bold" onClick={action('Menu Item 3')}>Menu Item 3</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user