mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge pull request #4830 from MetaMask/page-container-tabs
Add tabs support for PageContainer
This commit is contained in:
commit
619ab3838b
@ -665,7 +665,7 @@ describe('MetaMask', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('picks the newly created Test token', async () => {
|
it('picks the newly created Test token', async () => {
|
||||||
const addCustomToken = await findElement(driver, By.xpath("//div[contains(text(), 'Custom Token')]"))
|
const addCustomToken = await findElement(driver, By.xpath("//li[contains(text(), 'Custom Token')]"))
|
||||||
await addCustomToken.click()
|
await addCustomToken.click()
|
||||||
await delay(regularDelayMs)
|
await delay(regularDelayMs)
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@
|
|||||||
|
|
||||||
&--selected {
|
&--selected {
|
||||||
color: $curious-blue;
|
color: $curious-blue;
|
||||||
border-bottom: 3px solid $curious-blue;
|
border-bottom: 2px solid $curious-blue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
|
||||||
export default class PageContainerHeader extends Component {
|
export default class PageContainerHeader extends Component {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
subtitle: PropTypes.string,
|
subtitle: PropTypes.string,
|
||||||
@ -11,8 +11,18 @@ export default class PageContainerHeader extends Component {
|
|||||||
onBackButtonClick: PropTypes.func,
|
onBackButtonClick: PropTypes.func,
|
||||||
backButtonStyles: PropTypes.object,
|
backButtonStyles: PropTypes.object,
|
||||||
backButtonString: PropTypes.string,
|
backButtonString: PropTypes.string,
|
||||||
children: PropTypes.node,
|
tabs: PropTypes.node,
|
||||||
};
|
}
|
||||||
|
|
||||||
|
renderTabs () {
|
||||||
|
const { tabs } = this.props
|
||||||
|
|
||||||
|
return tabs && (
|
||||||
|
<ul className="page-container__tabs">
|
||||||
|
{ tabs }
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
renderHeaderRow () {
|
renderHeaderRow () {
|
||||||
const { showBackButton, onBackButtonClick, backButtonStyles, backButtonString } = this.props
|
const { showBackButton, onBackButtonClick, backButtonStyles, backButtonString } = this.props
|
||||||
@ -31,15 +41,18 @@ export default class PageContainerHeader extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { title, subtitle, onClose, children } = this.props
|
const { title, subtitle, onClose, tabs } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-container__header">
|
<div className={
|
||||||
|
classnames(
|
||||||
|
'page-container__header',
|
||||||
|
{ 'page-container__header--no-padding-bottom': Boolean(tabs) }
|
||||||
|
)
|
||||||
|
}>
|
||||||
|
|
||||||
{ this.renderHeaderRow() }
|
{ this.renderHeaderRow() }
|
||||||
|
|
||||||
{ children }
|
|
||||||
|
|
||||||
{
|
{
|
||||||
title && <div className="page-container__title">
|
title && <div className="page-container__title">
|
||||||
{ title }
|
{ title }
|
||||||
@ -59,6 +72,7 @@ export default class PageContainerHeader extends Component {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ this.renderTabs() }
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,82 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { PureComponent } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import PageContainerHeader from './page-container-header'
|
import PageContainerHeader from './page-container-header'
|
||||||
import PageContainerFooter from './page-container-footer'
|
import PageContainerFooter from './page-container-footer'
|
||||||
|
|
||||||
export default class PageContainer extends Component {
|
export default class PageContainer extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
// PageContainerHeader props
|
// PageContainerHeader props
|
||||||
title: PropTypes.string.isRequired,
|
backButtonString: PropTypes.string,
|
||||||
subtitle: PropTypes.string,
|
backButtonStyles: PropTypes.object,
|
||||||
|
onBackButtonClick: PropTypes.func,
|
||||||
onClose: PropTypes.func,
|
onClose: PropTypes.func,
|
||||||
showBackButton: PropTypes.bool,
|
showBackButton: PropTypes.bool,
|
||||||
onBackButtonClick: PropTypes.func,
|
subtitle: PropTypes.string,
|
||||||
backButtonStyles: PropTypes.object,
|
title: PropTypes.string.isRequired,
|
||||||
backButtonString: PropTypes.string,
|
// Tabs-related props
|
||||||
|
defaultActiveTabIndex: PropTypes.number,
|
||||||
|
tabsComponent: PropTypes.node,
|
||||||
// Content props
|
// Content props
|
||||||
ContentComponent: PropTypes.func,
|
contentComponent: PropTypes.node,
|
||||||
contentComponentProps: PropTypes.object,
|
|
||||||
// PageContainerFooter props
|
// PageContainerFooter props
|
||||||
onCancel: PropTypes.func,
|
|
||||||
cancelText: PropTypes.string,
|
cancelText: PropTypes.string,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
onCancel: PropTypes.func,
|
||||||
onSubmit: PropTypes.func,
|
onSubmit: PropTypes.func,
|
||||||
submitText: PropTypes.string,
|
submitText: PropTypes.string,
|
||||||
disabled: PropTypes.bool,
|
}
|
||||||
};
|
|
||||||
|
state = {
|
||||||
|
activeTabIndex: this.props.defaultActiveTabIndex || 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTabClick (activeTabIndex) {
|
||||||
|
this.setState({ activeTabIndex })
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTabs () {
|
||||||
|
const { tabsComponent } = this.props
|
||||||
|
|
||||||
|
if (!tabsComponent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const numberOfTabs = React.Children.count(tabsComponent.props.children)
|
||||||
|
|
||||||
|
return React.Children.map(tabsComponent.props.children, (child, tabIndex) => {
|
||||||
|
return child && React.cloneElement(child, {
|
||||||
|
onClick: index => this.handleTabClick(index),
|
||||||
|
tabIndex,
|
||||||
|
isActive: numberOfTabs > 1 && tabIndex === this.state.activeTabIndex,
|
||||||
|
key: tabIndex,
|
||||||
|
className: 'page-container__tab',
|
||||||
|
activeClassName: 'page-container__tab--selected',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
renderActiveTabContent () {
|
||||||
|
const { tabsComponent } = this.props
|
||||||
|
const { children } = tabsComponent.props
|
||||||
|
const { activeTabIndex } = this.state
|
||||||
|
|
||||||
|
return children[activeTabIndex]
|
||||||
|
? children[activeTabIndex].props.children
|
||||||
|
: children.props.children
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContent () {
|
||||||
|
const { contentComponent, tabsComponent } = this.props
|
||||||
|
|
||||||
|
if (contentComponent) {
|
||||||
|
return contentComponent
|
||||||
|
} else if (tabsComponent) {
|
||||||
|
return this.renderActiveTabContent()
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const {
|
const {
|
||||||
@ -35,8 +87,6 @@ export default class PageContainer extends Component {
|
|||||||
onBackButtonClick,
|
onBackButtonClick,
|
||||||
backButtonStyles,
|
backButtonStyles,
|
||||||
backButtonString,
|
backButtonString,
|
||||||
ContentComponent,
|
|
||||||
contentComponentProps,
|
|
||||||
onCancel,
|
onCancel,
|
||||||
cancelText,
|
cancelText,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
@ -54,9 +104,10 @@ export default class PageContainer extends Component {
|
|||||||
onBackButtonClick={onBackButtonClick}
|
onBackButtonClick={onBackButtonClick}
|
||||||
backButtonStyles={backButtonStyles}
|
backButtonStyles={backButtonStyles}
|
||||||
backButtonString={backButtonString}
|
backButtonString={backButtonString}
|
||||||
|
tabs={this.renderTabs()}
|
||||||
/>
|
/>
|
||||||
<div className="page-container__content">
|
<div className="page-container__content">
|
||||||
<ContentComponent { ...contentComponentProps } />
|
{ this.renderContent() }
|
||||||
</div>
|
</div>
|
||||||
<PageContainerFooter
|
<PageContainerFooter
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
@ -68,5 +119,4 @@ export default class PageContainer extends Component {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import classnames from 'classnames'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import ethUtil from 'ethereumjs-util'
|
import ethUtil from 'ethereumjs-util'
|
||||||
import { checkExistingAddresses } from './util'
|
import { checkExistingAddresses } from './util'
|
||||||
import { tokenInfoGetter } from '../../../token-util'
|
import { tokenInfoGetter } from '../../../token-util'
|
||||||
import { DEFAULT_ROUTE, CONFIRM_ADD_TOKEN_ROUTE } from '../../../routes'
|
import { DEFAULT_ROUTE, CONFIRM_ADD_TOKEN_ROUTE } from '../../../routes'
|
||||||
import Button from '../../button'
|
|
||||||
import TextField from '../../text-field'
|
import TextField from '../../text-field'
|
||||||
import TokenList from './token-list'
|
import TokenList from './token-list'
|
||||||
import TokenSearch from './token-search'
|
import TokenSearch from './token-search'
|
||||||
|
import PageContainer from '../../page-container'
|
||||||
|
import { Tabs, Tab } from '../../tabs'
|
||||||
|
|
||||||
const emptyAddr = '0x0000000000000000000000000000000000000000'
|
const emptyAddr = '0x0000000000000000000000000000000000000000'
|
||||||
const SEARCH_TAB = 'SEARCH'
|
const SEARCH_TAB = 'SEARCH'
|
||||||
@ -285,65 +285,33 @@ class AddToken extends Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderTabs () {
|
||||||
|
return (
|
||||||
|
<Tabs>
|
||||||
|
<Tab name={this.context.t('search')}>
|
||||||
|
{ this.renderSearchToken() }
|
||||||
|
</Tab>
|
||||||
|
<Tab name={this.context.t('customToken')}>
|
||||||
|
{ this.renderCustomTokenForm() }
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { displayedTab } = this.state
|
|
||||||
const { history, clearPendingTokens } = this.props
|
const { history, clearPendingTokens } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-container">
|
<PageContainer
|
||||||
<div className="page-container__header page-container__header--no-padding-bottom">
|
title={this.context.t('addTokens')}
|
||||||
<div className="page-container__title">
|
tabsComponent={this.renderTabs()}
|
||||||
{ this.context.t('addTokens') }
|
onSubmit={() => this.handleNext()}
|
||||||
</div>
|
disabled={this.hasError() || !this.hasSelected()}
|
||||||
<div className="page-container__tabs">
|
onCancel={() => {
|
||||||
<div
|
|
||||||
className={classnames('page-container__tab', {
|
|
||||||
'page-container__tab--selected': displayedTab === SEARCH_TAB,
|
|
||||||
})}
|
|
||||||
onClick={() => this.setState({ displayedTab: SEARCH_TAB })}
|
|
||||||
>
|
|
||||||
{ this.context.t('search') }
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={classnames('page-container__tab', {
|
|
||||||
'page-container__tab--selected': displayedTab === CUSTOM_TOKEN_TAB,
|
|
||||||
})}
|
|
||||||
onClick={() => this.setState({ displayedTab: CUSTOM_TOKEN_TAB })}
|
|
||||||
>
|
|
||||||
{ this.context.t('customToken') }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="page-container__content">
|
|
||||||
{
|
|
||||||
displayedTab === CUSTOM_TOKEN_TAB
|
|
||||||
? this.renderCustomTokenForm()
|
|
||||||
: this.renderSearchToken()
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div className="page-container__footer">
|
|
||||||
<Button
|
|
||||||
type="default"
|
|
||||||
large
|
|
||||||
className="page-container__footer-button"
|
|
||||||
onClick={() => {
|
|
||||||
clearPendingTokens()
|
clearPendingTokens()
|
||||||
history.push(DEFAULT_ROUTE)
|
history.push(DEFAULT_ROUTE)
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
{ this.context.t('cancel') }
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
large
|
|
||||||
className="page-container__footer-button"
|
|
||||||
onClick={() => this.handleNext()}
|
|
||||||
disabled={this.hasError() || !this.hasSelected()}
|
|
||||||
>
|
|
||||||
{ this.context.t('next') }
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@ import PropTypes from 'prop-types'
|
|||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
|
||||||
const Tab = props => {
|
const Tab = props => {
|
||||||
const { name, onClick, isActive, tabIndex } = props
|
const { name, onClick, isActive, tabIndex, className, activeClassName } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
className={classnames(
|
className={classnames(
|
||||||
'tab',
|
className,
|
||||||
isActive && 'tab--active',
|
{ [activeClassName]: isActive },
|
||||||
)}
|
)}
|
||||||
onClick={event => {
|
onClick={event => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
@ -26,6 +26,13 @@ Tab.propTypes = {
|
|||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
isActive: PropTypes.bool,
|
isActive: PropTypes.bool,
|
||||||
tabIndex: PropTypes.number,
|
tabIndex: PropTypes.number,
|
||||||
|
className: PropTypes.string,
|
||||||
|
activeClassName: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
Tab.defaultProps = {
|
||||||
|
className: 'tab',
|
||||||
|
activeClassName: 'tab--active',
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Tab
|
export default Tab
|
||||||
|
Loading…
x
Reference in New Issue
Block a user