1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Move asset list to home tab on small screens (#8264)

Two tabs have been created on the home screen: 'Assets' and 'History'.
This tabbed view is shown only on small screens (e.g. in the popup).
The fullscreen view is unchanged.

The toggle-able left sidebar no longer exists, so some 'sidebar-left'
specific code and styles have been removed. The button in the menu bar
has been removed as well.

The 'History' title of the transaction history is now redundant when
where are no pending transactions, so it as been conditionally hidden.

A passthrough for `data-testid` has been added to the Tab component for
convenience in e2e tests.
This commit is contained in:
Mark Stacey 2020-04-01 13:35:07 -03:00 committed by GitHub
parent dbc7446b9a
commit cb0ab90c84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 97 additions and 318 deletions

View File

@ -657,9 +657,6 @@
"memo": {
"message": "አጭር ማስታወሻ"
},
"menu": {
"message": "ምናሌ"
},
"message": {
"message": "መልዕክት"
},

View File

@ -653,9 +653,6 @@
"memo": {
"message": "مذكرة"
},
"menu": {
"message": "القائمة"
},
"message": {
"message": "رسالة"
},

View File

@ -656,9 +656,6 @@
"memo": {
"message": "бележка"
},
"menu": {
"message": "Меню"
},
"message": {
"message": "Съобщение"
},

View File

@ -660,9 +660,6 @@
"memo": {
"message": "মেমো"
},
"menu": {
"message": "মেনু"
},
"message": {
"message": "বার্তা"
},

View File

@ -644,9 +644,6 @@
"memo": {
"message": "notes"
},
"menu": {
"message": "Menú"
},
"message": {
"message": "Missatge"
},

View File

@ -636,9 +636,6 @@
"memo": {
"message": " Memo"
},
"menu": {
"message": "Menü"
},
"message": {
"message": "Nachricht"
},

View File

@ -657,9 +657,6 @@
"memo": {
"message": "σημείωμα"
},
"menu": {
"message": "Μενού"
},
"message": {
"message": "Μήνυμα"
},

View File

@ -837,9 +837,6 @@
"memo": {
"message": "memo"
},
"menu": {
"message": "Menu"
},
"message": {
"message": "Message"
},

View File

@ -536,9 +536,6 @@
"mainnet": {
"message": "Red principal de Ethereum (Main Net)"
},
"menu": {
"message": "Menú"
},
"message": {
"message": "Mensaje"
},

View File

@ -645,9 +645,6 @@
"memorizePhrase": {
"message": "Memoriza esta frase."
},
"menu": {
"message": "Menú"
},
"message": {
"message": "Mensaje"
},

View File

@ -650,9 +650,6 @@
"memo": {
"message": "teatis"
},
"menu": {
"message": "Menüü"
},
"message": {
"message": "Sõnum"
},

View File

@ -660,9 +660,6 @@
"memo": {
"message": "یادداشت"
},
"menu": {
"message": "مینو"
},
"message": {
"message": "پیام"
},

View File

@ -657,9 +657,6 @@
"memo": {
"message": "muistio"
},
"menu": {
"message": "Valikko"
},
"message": {
"message": "Viesti"
},

View File

@ -657,9 +657,6 @@
"memo": {
"message": "תזכיר"
},
"menu": {
"message": "תפריט"
},
"message": {
"message": "הודעה"
},

View File

@ -657,9 +657,6 @@
"memo": {
"message": "ज्ञापन"
},
"menu": {
"message": "मेनू"
},
"message": {
"message": "संदेश"
},

View File

@ -653,9 +653,6 @@
"memo": {
"message": "podsjetnik"
},
"menu": {
"message": "Izbornik"
},
"message": {
"message": "Poruka"
},

View File

@ -389,9 +389,6 @@
"mainnet": {
"message": "Prensipal Ethereum Rezo a"
},
"menu": {
"message": "Opsyon"
},
"message": {
"message": "Mesaje"
},

View File

@ -653,9 +653,6 @@
"memo": {
"message": "emlékeztető"
},
"menu": {
"message": "Menü"
},
"message": {
"message": "Üzenet"
},

View File

@ -822,9 +822,6 @@
"memo": {
"message": "promemoria"
},
"menu": {
"message": "Menu"
},
"message": {
"message": "Messaggio"
},

View File

@ -323,9 +323,6 @@
"mainnet": {
"message": "Ethereumメインネットワーク"
},
"menu": {
"message": "メニュー"
},
"message": {
"message": "メッセージ"
},

View File

@ -660,9 +660,6 @@
"memo": {
"message": "ಮೆಮೊ"
},
"menu": {
"message": "ಮೆನು"
},
"message": {
"message": "ಸಂದೇಶ"
},

View File

@ -654,9 +654,6 @@
"memo": {
"message": "메모"
},
"menu": {
"message": "메뉴"
},
"message": {
"message": "메시지"
},

View File

@ -660,9 +660,6 @@
"memo": {
"message": "pastaba"
},
"menu": {
"message": "Meniu"
},
"message": {
"message": "Pranešimas"
},

View File

@ -656,9 +656,6 @@
"memo": {
"message": "atgādinājums"
},
"menu": {
"message": "Izvēlne"
},
"message": {
"message": "Ziņojums"
},

View File

@ -650,9 +650,6 @@
"memo": {
"message": "beskjed"
},
"menu": {
"message": "Meny "
},
"message": {
"message": "Melding "
},

View File

@ -647,9 +647,6 @@
"memo": {
"message": "notă"
},
"menu": {
"message": "Meniu"
},
"message": {
"message": "Mesaj"
},

View File

@ -890,9 +890,6 @@
"memo": {
"message": "Мнемотик"
},
"menu": {
"message": "Меню"
},
"metamaskVersion": {
"message": "Версия MetaMask"
},

View File

@ -645,9 +645,6 @@
"memo": {
"message": "okrožnica"
},
"menu": {
"message": "Meni"
},
"message": {
"message": "Sporočilo"
},

View File

@ -651,9 +651,6 @@
"memorizePhrase": {
"message": "Zapamtite ovaj izraz."
},
"menu": {
"message": "Meni"
},
"message": {
"message": "Poruka"
},

View File

@ -644,9 +644,6 @@
"memorizePhrase": {
"message": "Memorera denna fras."
},
"menu": {
"message": "Meny"
},
"message": {
"message": "Meddelande"
},

View File

@ -638,9 +638,6 @@
"memorizePhrase": {
"message": "Kariri kirai hiki"
},
"menu": {
"message": "Menyu"
},
"message": {
"message": "Ujumbe"
},

View File

@ -660,9 +660,6 @@
"memo": {
"message": "нотатка"
},
"menu": {
"message": "Меню"
},
"message": {
"message": "Повідомлення"
},

View File

@ -648,9 +648,6 @@
"memo": {
"message": "备忘录"
},
"menu": {
"message": "菜单"
},
"message": {
"message": "消息"
},

View File

@ -654,9 +654,6 @@
"memo": {
"message": "備註"
},
"menu": {
"message": "選單"
},
"message": {
"message": "訊息"
},

View File

@ -205,10 +205,10 @@ describe('MetaMask', function () {
it('confirms the transaction', async function () {
await driver.clickElement(By.xpath(`//button[contains(text(), 'Confirm')]`))
await driver.delay(largeDelayMs)
})
it('finds the transaction in the transactions list', async function () {
await driver.clickElement(By.css('[data-testid="home__history-tab"]'))
await driver.wait(async () => {
const confirmedTxes = await driver.findElements(By.css('.transaction-list__completed-transactions .transaction-list-item'))
return confirmedTxes.length === 1

View File

@ -18,21 +18,17 @@ export default class AssetList extends Component {
}
static propTypes = {
hideSidebar: PropTypes.func.isRequired,
history: PropTypes.object.isRequired,
selectedAccount: PropTypes.object,
selectedTokenAddress: PropTypes.string,
setSelectedToken: PropTypes.func.isRequired,
sidebarOpen: PropTypes.bool.isRequired,
unsetSelectedToken: PropTypes.func.isRequired,
}
renderWalletBalance () {
const {
hideSidebar,
selectedTokenAddress,
selectedAccount,
sidebarOpen,
unsetSelectedToken,
} = this.props
@ -46,7 +42,6 @@ export default class AssetList extends Component {
className="wallet-balance"
onClick={() => {
unsetSelectedToken()
selectedTokenAddress && sidebarOpen && hideSidebar()
}}
>
<BalanceComponent
@ -59,9 +54,7 @@ export default class AssetList extends Component {
renderAddToken () {
const {
hideSidebar,
history,
sidebarOpen,
} = this.props
const { metricsEvent } = this.context
@ -76,21 +69,13 @@ export default class AssetList extends Component {
name: 'Clicked "Add Token"',
},
})
if (sidebarOpen) {
hideSidebar()
}
}}
/>
)
}
render () {
const {
hideSidebar,
selectedTokenAddress,
setSelectedToken,
sidebarOpen,
} = this.props
const { setSelectedToken } = this.props
return (
<>
{this.renderWalletBalance()}
@ -104,7 +89,6 @@ export default class AssetList extends Component {
name: 'Clicked Token',
},
})
selectedTokenAddress !== tokenAddress && sidebarOpen && hideSidebar()
}}
/>
{this.renderAddToken()}

View File

@ -2,12 +2,11 @@ import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import { compose } from 'redux'
import AssetList from './asset-list.component'
import { hideSidebar, setSelectedToken } from '../../../store/actions'
import { setSelectedToken } from '../../../store/actions'
import { getSelectedAccount } from '../../../selectors/selectors'
function mapStateToProps (state) {
return {
sidebarOpen: state.appState.sidebar.isOpen,
selectedAccount: getSelectedAccount(state),
selectedTokenAddress: state.metamask.selectedTokenAddress,
}
@ -15,7 +14,6 @@ function mapStateToProps (state) {
function mapDispatchToProps (dispatch) {
return {
hideSidebar: () => dispatch(hideSidebar()),
setSelectedToken: (tokenAddress) => dispatch(setSelectedToken(tokenAddress)),
unsetSelectedToken: () => dispatch(setSelectedToken()),
}

View File

@ -1 +1 @@
export { default } from './menu-bar.container'
export { default } from './menu-bar.component'

View File

@ -10,39 +10,14 @@ export default class MenuBar extends PureComponent {
metricsEvent: PropTypes.func,
}
static propTypes = {
hideSidebar: PropTypes.func,
sidebarOpen: PropTypes.bool,
showSidebar: PropTypes.func,
}
state = { accountDetailsMenuOpen: false }
render () {
const { t } = this.context
const { sidebarOpen, hideSidebar, showSidebar } = this.props
const { accountDetailsMenuOpen } = this.state
return (
<div className="menu-bar">
<Tooltip
title={t('menu')}
position="bottom"
>
<div
className="menu-bar__sidebar-button"
onClick={() => {
this.context.metricsEvent({
eventOpts: {
category: 'Navigation',
action: 'Home',
name: 'Opened Hamburger',
},
})
sidebarOpen ? hideSidebar() : showSidebar()
}}
/>
</Tooltip>
<SelectedAccount />
<Tooltip

View File

@ -1,26 +0,0 @@
import { connect } from 'react-redux'
import { WALLET_VIEW_SIDEBAR } from '../sidebars/sidebar.constants'
import MenuBar from './menu-bar.component'
import { showSidebar, hideSidebar } from '../../../store/actions'
const mapStateToProps = (state) => {
const { appState: { sidebar: { isOpen } } } = state
return {
sidebarOpen: isOpen,
}
}
const mapDispatchToProps = (dispatch) => {
return {
showSidebar: () => {
dispatch(showSidebar({
transitionName: 'sidebar-right',
type: WALLET_VIEW_SIDEBAR,
}))
},
hideSidebar: () => dispatch(hideSidebar()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MenuBar)

View File

@ -1,88 +1,53 @@
import React from 'react'
import configureStore from 'redux-mock-store'
import assert from 'assert'
import sinon from 'sinon'
import { Provider } from 'react-redux'
import configureStore from 'redux-mock-store'
import { mountWithRouter } from '../../../../../../test/lib/render-helpers'
import MenuBar from '../index'
import { Provider } from 'react-redux'
const initState = {
metamask: {
network: '1',
selectedAddress: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
identities: {
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': {
address: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
name: 'Account 1',
},
},
keyrings: [
{
type: 'HD Key Tree',
accounts: [
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
],
},
],
frequentRpcListDetail: [],
},
}
const mockStore = configureStore()
describe('MenuBar', function () {
let wrapper
const mockStore = {
metamask: {
network: '1',
selectedAddress: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
identities: {
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': {
address: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
name: 'Account 1',
},
},
keyrings: [
{
type: 'HD Key Tree',
accounts: [
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
],
},
],
frequentRpcListDetail: [],
},
appState: {
sidebar: {
isOpen: false,
},
},
}
const store = configureStore()(mockStore)
afterEach(function () {
sinon.restore()
})
it('shows side bar when sidbarOpen is set to false', function () {
const props = {
showSidebar: sinon.spy(),
}
wrapper = mountWithRouter(
<Provider store={store}>
<MenuBar.WrappedComponent {...props} />
</Provider>, store
)
const sidebarButton = wrapper.find('.menu-bar__sidebar-button')
sidebarButton.simulate('click')
assert(props.showSidebar.calledOnce)
})
it('hides side when sidebarOpen is set to true', function () {
const props = {
showSidebar: sinon.spy(),
hideSidebar: sinon.spy(),
sidebarOpen: true,
}
wrapper = mountWithRouter(
<Provider store={store}>
<MenuBar.WrappedComponent {...props} />
</Provider>, store
)
const sidebarButton = wrapper.find('.menu-bar__sidebar-button')
sidebarButton.prop('onClick')()
assert(props.hideSidebar.calledOnce)
})
it('opens account detail menu when account options is clicked', function () {
const store = mockStore(initState)
const wrapper = mountWithRouter(
<Provider store={store}>
<MenuBar />
</Provider>
)
const accountOptions = wrapper.find('.menu-bar__open-in-browser')
accountOptions.simulate('click')
assert.equal(wrapper.find('MenuBar').instance().state.accountDetailsMenuOpen, true)
})
it('sets accountDetailsMenuOpen to false when closed', function () {
const store = mockStore(initState)
const wrapper = mountWithRouter(
<Provider store={store}>
<MenuBar />
</Provider>
)
wrapper.find('MenuBar').instance().setState({ accountDetailsMenuOpen: true })
wrapper.update()

View File

@ -1,8 +1,6 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import ReactCSSTransitionGroup from 'react-transition-group/CSSTransitionGroup'
import WalletView from '../wallet-view'
import { WALLET_VIEW_SIDEBAR } from './sidebar.constants'
import CustomizeGas from '../gas-customization/gas-modal-page-container'
export default class Sidebar extends Component {
@ -35,8 +33,6 @@ export default class Sidebar extends Component {
const { type, sidebarProps = {} } = this.props
const { transaction = {} } = sidebarProps
switch (type) {
case WALLET_VIEW_SIDEBAR:
return <WalletView responsiveDisplayClassname="sidebar-right" />
case 'customize-gas':
return <div className="sidebar-left"><CustomizeGas transaction={transaction} /></div>
default:

View File

@ -1 +0,0 @@
export const WALLET_VIEW_SIDEBAR = 'wallet-view'

View File

@ -5,7 +5,6 @@ import sinon from 'sinon'
import ReactCSSTransitionGroup from 'react-transition-group/CSSTransitionGroup'
import Sidebar from '../sidebar.component.js'
import WalletView from '../../wallet-view'
import CustomizeGas from '../../gas-customization/gas-modal-page-container'
const propsMethodSpies = {
@ -21,7 +20,7 @@ describe('Sidebar Component', function () {
sidebarOpen={false}
hideSidebar={propsMethodSpies.hideSidebar}
transitionName="someTransition"
type="wallet-view"
type="customize-gas"
/>
))
})
@ -52,18 +51,10 @@ describe('Sidebar Component', function () {
let renderSidebarContent
beforeEach(function () {
wrapper.setProps({ type: 'wallet-view' })
renderSidebarContent = wrapper.instance().renderSidebarContent()
})
it('should render sidebar content with the type wallet-view', function () {
wrapper.setProps({ type: 'wallet-view' })
renderSidebarContent = wrapper.instance().renderSidebarContent()
assert.equal(renderSidebarContent.props.responsiveDisplayClassname, 'sidebar-right')
})
it('should render sidebar content with the type customize-gas', function () {
wrapper.setProps({ type: 'customize-gas' })
renderSidebarContent = wrapper.instance().renderSidebarContent()
const renderedSidebarContent = shallow(renderSidebarContent)
assert(renderedSidebarContent.hasClass('sidebar-left'))
@ -93,7 +84,8 @@ describe('Sidebar Component', function () {
assert.equal(wrapper.children().length, 2)
assert(wrapper.children().at(1).hasClass('sidebar-overlay'))
assert.equal(wrapper.children().at(0).children().length, 1)
assert(wrapper.children().at(0).children().at(0).is(WalletView))
assert(wrapper.children().at(0).children().at(0).hasClass('sidebar-left'))
assert(wrapper.children().at(0).children().at(0).children().at(0).is(CustomizeGas))
})
})
})

View File

@ -22,11 +22,6 @@ describe('Token Cell', function () {
},
conversionRate: 7.00,
},
appState: {
sidebar: {
isOpen: true,
},
},
}
const middlewares = [thunk]

View File

@ -100,9 +100,13 @@ export default class TransactionList extends PureComponent {
)
}
<div className="transaction-list__completed-transactions">
<div className="transaction-list__header">
{ t('history') }
</div>
{
pendingLength > 0 && (
<div className="transaction-list__header">
{ t('history') }
</div>
)
}
{
completedTransactions.length > 0
? completedTransactions.map((transactionGroup, index) => (

View File

@ -18,25 +18,16 @@ export default class WalletView extends Component {
}
static propTypes = {
hideSidebar: PropTypes.func.isRequired,
history: PropTypes.object.isRequired,
identities: PropTypes.object.isRequired,
keyrings: PropTypes.array.isRequired,
responsiveDisplayClassname: PropTypes.string,
selectedAddress: PropTypes.string.isRequired,
sidebarOpen: PropTypes.bool.isRequired,
}
showConnectedSites = () => {
const {
sidebarOpen,
hideSidebar,
history,
} = this.props
const { history } = this.props
history.push(CONNECTED_ROUTE)
if (sidebarOpen) {
hideSidebar()
}
}
render () {

View File

@ -2,25 +2,17 @@ import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import { compose } from 'redux'
import WalletView from './wallet-view.component'
import { hideSidebar } from '../../../store/actions'
import { getSelectedAddress } from '../../../selectors/selectors'
function mapStateToProps (state) {
return {
sidebarOpen: state.appState.sidebar.isOpen,
identities: state.metamask.identities,
keyrings: state.metamask.keyrings,
selectedAddress: getSelectedAddress(state),
}
}
function mapDispatchToProps (dispatch) {
return {
hideSidebar: () => dispatch(hideSidebar()),
}
}
export default compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
connect(mapStateToProps)
)(WalletView)

View File

@ -5,6 +5,5 @@
display: flex;
justify-content: flex-start;
border-bottom: 1px solid $geyser;
padding: 0 16px;
}
}

View File

@ -3,7 +3,14 @@ import PropTypes from 'prop-types'
import classnames from 'classnames'
const Tab = (props) => {
const { name, onClick, isActive, tabIndex, className } = props
const {
className,
'data-testid': dataTestId,
isActive,
name,
onClick,
tabIndex,
} = props
return (
<li
@ -12,6 +19,7 @@ const Tab = (props) => {
className,
{ 'tab--active': isActive },
)}
data-testid={dataTestId}
onClick={(event) => {
event.preventDefault()
onClick(tabIndex)
@ -24,6 +32,7 @@ const Tab = (props) => {
Tab.propTypes = {
className: PropTypes.string,
'data-testid': PropTypes.string,
isActive: PropTypes.bool, // required, but added using React.cloneElement
name: PropTypes.string.isRequired,
onClick: PropTypes.func,

View File

@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
import Media from 'react-media'
import { Redirect, Route } from 'react-router-dom'
import { formatDate } from '../../helpers/utils/util'
import AssetList from '../../components/app/asset-list'
import HomeNotification from '../../components/app/home-notification'
import DaiMigrationNotification from '../../components/app/dai-migration-component'
import MultipleNotifications from '../../components/app/multiple-notifications'
@ -11,6 +12,7 @@ import TransactionList from '../../components/app/transaction-list'
import TransactionViewBalance from '../../components/app/transaction-view-balance'
import MenuBar from '../../components/app/menu-bar'
import ConnectedSites from '../connected-sites'
import { Tabs, Tab } from '../../components/ui/tabs'
import {
RESTORE_VAULT_ROUTE,
@ -179,18 +181,34 @@ export default class Home extends PureComponent {
>
{
(isWideViewport) => (
<>
{ isWideViewport ? <WalletView /> : null }
<div className="home__main-view">
{
!isWideViewport ? <MenuBar /> : null
}
<div className="home__balance-wrapper">
<TransactionViewBalance />
isWideViewport
? (
<>
<WalletView />
<div className="home__main-view">
<div className="home__balance-wrapper">
<TransactionViewBalance />
</div>
<TransactionList />
</div>
</>
)
: (
<div className="home__main-view">
<MenuBar />
<div className="home__balance-wrapper">
<TransactionViewBalance />
</div>
<Tabs>
<Tab className="home__tab" data-testid="home__asset-tab" name="Assets">
<AssetList />
</Tab>
<Tab className="home__tab" data-testid="home__history-tab" name="History">
<TransactionList />
</Tab>
</Tabs>
</div>
<TransactionList />
</div>
</>
)
)
}
</Media>

View File

@ -32,4 +32,8 @@
flex: 0 0 auto;
}
}
&__tab {
flex-grow: 1;
}
}

View File

@ -8,7 +8,6 @@ import FirstTimeFlow from '../first-time-flow'
import SendTransactionScreen from '../send'
import ConfirmTransaction from '../confirm-transaction'
import Sidebar from '../../components/app/sidebars'
import { WALLET_VIEW_SIDEBAR } from '../../components/app/sidebars/sidebar.constants'
import Home from '../home'
import Settings from '../settings'
import Authenticated from '../../helpers/higher-order-components/authenticated'
@ -205,18 +204,6 @@ export default class Routes extends Component {
} = sidebar
const { transaction: sidebarTransaction } = props || {}
const sidebarOnOverlayClose = sidebarType === WALLET_VIEW_SIDEBAR
? () => {
this.context.metricsEvent({
eventOpts: {
category: 'Navigation',
action: 'Wallet Sidebar',
name: 'Closed Sidebare Via Overlay',
},
})
}
: null
const sidebarShouldClose = sidebarTransaction &&
!sidebarTransaction.status === 'failed' &&
!submittedPendingTransactions.find(({ id }) => id === sidebarTransaction.id)
@ -250,7 +237,6 @@ export default class Routes extends Component {
transitionName={sidebarTransitionName}
type={sidebarType}
sidebarProps={sidebar.props}
onOverlayClose={sidebarOnOverlayClose}
/>
<NetworkDropdown
provider={provider}

View File

@ -24,7 +24,7 @@ export const actionConstants = {
// sidebar state
SIDEBAR_OPEN: 'UI_SIDEBAR_OPEN',
SIDEBAR_CLOSE: 'UI_SIDEBAR_CLOSE',
// sidebar state
// alert state
ALERT_OPEN: 'UI_ALERT_OPEN',
ALERT_CLOSE: 'UI_ALERT_CLOSE',
QR_CODE_DETECTED: 'UI_QR_CODE_DETECTED',