Skip to content

Commit

Permalink
Merge branch 'development' into 2466-reolve-ux-issues-voting
Browse files Browse the repository at this point in the history
  • Loading branch information
reyraa committed Nov 8, 2019
2 parents a0ebb0a + de71fa0 commit bdeff1f
Show file tree
Hide file tree
Showing 21 changed files with 523 additions and 54 deletions.
5 changes: 5 additions & 0 deletions i18n/locales/en/common.json
Expand Up @@ -18,6 +18,7 @@
"Account Balance: {{balance}} {{token}}": "Account Balance: {{balance}} {{token}}",
"Account name": "Account name",
"Account nickname": "Account nickname",
"Accounts": "Accounts",
"Activate ({{ fee }} LSK Fee)": "Activate ({{ fee }} LSK Fee)",
"Active": "Active",
"Add a new bookmark": "Add a new bookmark",
Expand Down Expand Up @@ -291,6 +292,7 @@
"Oops, looks like something went wrong. Please try again.": "Oops, looks like something went wrong. Please try again.",
"Opt-in to sharing anonymous data in order to improve Lisk.": "Opt-in to sharing anonymous data in order to improve Lisk.",
"Outgoing transactions": "Outgoing transactions",
"Owner": "Owner",
"Paper wallet": "Paper wallet",
"Passphrase": "Passphrase",
"Passphrase is not valid": "Passphrase is not valid",
Expand Down Expand Up @@ -351,6 +353,7 @@
"Save changes": "Save changes",
"Save it on an encrypted hard drive: USB key or a backup drive": "Save it on an encrypted hard drive: USB key or a backup drive",
"Save your passphrase": "Save your passphrase",
"Scan address": "Scan address",
"Search within the network...": "Search within the network...",
"Second passphrase": "Second passphrase",
"Second passphrase is an optional extra layer of protection to your account. You can register at anytime, but you can not remove it.": "Second passphrase is an optional extra layer of protection to your account. You can register at anytime, but you can not remove it.",
Expand Down Expand Up @@ -400,6 +403,7 @@
"Sum of LSK in all accounts who have voted for this delegate.": "Sum of LSK in all accounts who have voted for this delegate.",
"Sum of all LSK awarded to a delegate for each block successfully generated on the blockchain.": "Sum of all LSK awarded to a delegate for each block successfully generated on the blockchain.",
"Summary of delegate registration": "Summary of delegate registration",
"Supply": "Supply",
"Terms of Use": "Terms of Use",
"Testnet": "Testnet",
"The message can't contain whitespace at the beginning or end.": "The message can't contain whitespace at the beginning or end.",
Expand Down Expand Up @@ -451,6 +455,7 @@
"Upvotes": "Upvotes",
"Use the sharing link to easily request any amount of LSK from Lisk users.": "Use the sharing link to easily request any amount of LSK from Lisk users.",
"Use this tool to verify the validity of a signed message. This allows you to ensure that the person who signed the message was in fact the account owner": "Use this tool to verify the validity of a signed message. This allows you to ensure that the person who signed the message was in fact the account owner",
"Verify address": "Verify address",
"Verify message": "Verify message",
"Version": "Version",
"View": "View",
Expand Down
20 changes: 20 additions & 0 deletions src/assets/images/icons/copy-active.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/assets/images/icons/copy.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions src/assets/images/icons/qr-code-active.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions src/assets/images/icons/qr-code.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions src/assets/images/icons/verify-icon-active.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions src/components/screens/monitor/accounts/accounts.js
@@ -0,0 +1,18 @@
import React from 'react';
import MonitorHeader from '../header';
import AccountsTable from './accountsTable';

const MonitorAccounts = ({ t, accounts, networkStatus }) => (
<div>
<MonitorHeader />
<AccountsTable
isLoadMoreEnabled
title={t('Accounts')}
accounts={accounts}
networkStatus={networkStatus}
t={t}
/>
</div>
);

export default MonitorAccounts;
76 changes: 76 additions & 0 deletions src/components/screens/monitor/accounts/accounts.test.js
@@ -0,0 +1,76 @@
import React from 'react';
import { mount } from 'enzyme';
import Delegates from './accounts';
import accounts from '../../../../../test/constants/accounts';

jest.mock('../../../../constants/monitor', () => ({ DEFAULT_LIMIT: 4 }));

const accountsApiResponse = Object.values(accounts);

describe('Top Accounts Monitor Page', () => {
let props;
let accountsWithData;
let wrapper;

const setup = properties => mount(<Delegates {...properties} />);

beforeEach(() => {
props = {
t: key => key,
accounts: {
isLoading: true,
data: [],
loadData: jest.fn(),
clearData: jest.fn(),
urlSearchParams: {},
},
networkStatus: {
data: {
supply: 9999999999999,
},
},
};

accountsWithData = {
...props.accounts,
isLoading: false,
data: [
{
address: '1234567L',
delegate: {
username: 'geenesis',
},
knowledge: {
owner: 'Lisk',
description: 'assetes',
},
},
...accountsApiResponse,
],
};

wrapper = setup(props);
});

it('renders a page with header', () => {
expect(wrapper.find('Box header')).toIncludeText('Accounts');
});

it('renders table with accounts', () => {
expect(wrapper.find('.accounts-row')).toHaveLength(0);
wrapper.setProps({ accounts: accountsWithData });
expect(wrapper.find('.accounts-row').hostNodes()).toHaveLength(accountsApiResponse.length + 1);
});

it('shows error if API failed', () => {
const error = 'Loading failed';
wrapper.setProps({
accounts: {
...props.accounts,
isLoading: false,
error,
},
});
expect(wrapper).toIncludeText(error);
});
});
11 changes: 11 additions & 0 deletions src/components/screens/monitor/accounts/accountsTable.css
@@ -0,0 +1,11 @@
@import '../../../../app/variables.css';

.content {
padding: 0 !important;
counter-reset: section;
}

.counter::before {
counter-increment: section;
content: "#" counter(section);
}
134 changes: 134 additions & 0 deletions src/components/screens/monitor/accounts/accountsTable.js
@@ -0,0 +1,134 @@
import React from 'react';
import { BigNumber } from 'bignumber.js';
import grid from 'flexboxgrid/dist/flexboxgrid.css';
import { tokenMap } from '../../../../constants/tokens';
import Box from '../../../toolbox/box';
import BoxHeader from '../../../toolbox/box/header';
import BoxContent from '../../../toolbox/box/content';
import BoxFooterButton from '../../../toolbox/box/footerButton';
import BoxEmptyState from '../../../toolbox/box/emptyState';
import LiskAmount from '../../../shared/liskAmount';
import Illustration from '../../../toolbox/illustration';
import withResizeValues from '../../../../utils/withResizeValues';
import routes from '../../../../constants/routes';
import Table from '../../../toolbox/table';
import AccountVisualWithAddress from '../../../shared/accountVisualWithAddress';
import { DEFAULT_LIMIT } from '../../../../constants/monitor';
import { formatAmountBasedOnLocale } from '../../../../utils/formattedNumber';
import styles from './accountsTable.css';

class AccountsTable extends React.Component {
handleLoadMore = () => {
const { accounts } = this.props;
accounts.loadData({ offset: accounts.data.length });
}

render() {
const {
accounts,
isMediumViewPort,
networkStatus,
t,
title,
} = this.props;
const supply = networkStatus.data.supply;

return (
<Box main isLoading={accounts.isLoading} className="accounts-box">
<BoxHeader>
<h1>{title}</h1>
</BoxHeader>
{
accounts.error
? (
<BoxContent>
<BoxEmptyState>
<Illustration name="emptyWallet" />
<h3>{`${accounts.error}`}</h3>
</BoxEmptyState>
</BoxContent>
)
: (
<React.Fragment>
<BoxContent className={styles.content}>
<Table
getRowLink={account => `${routes.accounts.path}/${account.address}`}
data={accounts.data}
columns={[
{
header: t('Rank'),
className: `${grid['col-xs-1']} ${grid['col-md-1']}`,
id: 'rank',
getValue: () => (<span className={styles.counter} />),
},
{
header: t('Address'),
className: `${grid['col-xs-3']} ${grid['col-md-5']}`,
id: 'address',
getValue: account => (
<AccountVisualWithAddress
address={account.address}
isMediumViewPort={isMediumViewPort}
transactionSubject="address"
showBookmarkedAddress
/>
),
},
{
header: t('Balance'),
className: `${grid['col-xs-3']} ${grid['col-md-3']}`,
id: 'balance',
getValue: account => (
<LiskAmount val={account.balance} roundTo={0} token={tokenMap.LSK.key} />
),
},
{
header: t('Supply'),
className: `${grid['col-xs-2']} ${grid['col-md-1']}`,
id: 'supply',
getValue: (account) => {
const amount = new BigNumber(account.balance / supply * 100);
return <span>{`${formatAmountBasedOnLocale({ value: amount.toFormat(2) })} %`}</span>;
},
},
{
header: t('Owner'),
className: `${grid['col-xs-3']} ${grid['col-md-2']}`,
id: 'owner',
getValue: (account) => {
const delegateUsername = account.delegate ? account.delegate.username : '';
const text = account.knowledge
&& account.knowledge.owner && account.knowledge.description
? `${account.knowledge.owner} ${account.knowledge.description}`
: delegateUsername;
return text;
},
},
]}
rowClassName="accounts-row"
rowKey="address"
/>
</BoxContent>
{
!!accounts.data.length
&& accounts.data.length % DEFAULT_LIMIT === 0
? (
<BoxFooterButton className="load-more" onClick={this.handleLoadMore}>
{t('Load more')}
</BoxFooterButton>
)
: null
}
</React.Fragment>
)
}
</Box>
);
}
}

AccountsTable.defaultProps = {
title: '',
};

export default withResizeValues(AccountsTable);

0 comments on commit bdeff1f

Please sign in to comment.