Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions packages/extension-ui/src/Popup/Accounts/Account.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import Adapter from 'enzyme-adapter-react-16';
import { configure, mount, ReactWrapper } from 'enzyme';
import { Link, defaultTheme, Theme } from '@polkadot/extension-ui/components';
import { defaultTheme, Theme } from '@polkadot/extension-ui/components';
import { MemoryRouter } from 'react-router';
import React from 'react';

Expand All @@ -28,18 +28,20 @@ describe('Account component', () => {

it('shows Export option if account is not external', () => {
wrapper = mountAccountComponent({ isExternal: false });
wrapper.find('Details').simulate('click');

expect(wrapper.find(Link).length).toBe(3);
expect(wrapper.find(Link).at(0).text()).toContain('Forget');
expect(wrapper.find(Link).at(1).text()).toContain('Export');
expect(wrapper.find(Link).at(2).text()).toContain('Edit');
expect(wrapper.find('MenuItem').length).toBe(3);
expect(wrapper.find('MenuItem').at(0).text()).toBe('Rename');
expect(wrapper.find('MenuItem').at(1).text()).toBe('Export Account');
expect(wrapper.find('MenuItem').at(2).text()).toBe('Forget Account');
});

it('does not show Export option if account is external', () => {
wrapper = mountAccountComponent({ isExternal: true });
wrapper.find('Details').simulate('click');

expect(wrapper.find(Link).length).toBe(2);
expect(wrapper.find(Link).at(0).text()).toContain('Forget');
expect(wrapper.find(Link).at(1).text()).toContain('Edit');
expect(wrapper.find('MenuItem').length).toBe(2);
expect(wrapper.find('MenuItem').at(0).text()).toBe('Rename');
expect(wrapper.find('MenuItem').at(1).text()).toBe('Forget Account');
});
});
45 changes: 31 additions & 14 deletions packages/extension-ui/src/Popup/Accounts/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { AccountJson } from '@polkadot/extension/background/types';
import React, { useContext, useState } from 'react';
import styled from 'styled-components';

import { ActionBar, ActionContext, Address, Link } from '../../components';
import { ActionContext, Address, Link } from '../../components';
import { editAccount } from '../../messaging';
import { Name } from '../../partials';

Expand All @@ -32,11 +32,19 @@ function Account ({ address, className, isExternal }: Props): React.ReactElement
_toggleEdit();
};

return (
return <div className={className}>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the className is not used here anymore.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used for passing styled-component styles

<Address
address={address}
className={className}
name={editedName}
actions={
<>
<MenuGroup>
<MenuItem onClick={_toggleEdit}>Rename</MenuItem>
</MenuGroup>
{!isExternal && <MenuItem isDanger to={`/account/export/${address}`}>Export Account</MenuItem>}
<MenuItem isDanger to={`/account/forget/${address}`}>Forget Account</MenuItem>
</>
}
>
{isEditing && (
<Name
Expand All @@ -48,22 +56,31 @@ function Account ({ address, className, isExternal }: Props): React.ReactElement
onChange={setName}
/>
)}
<ActionBar>
<div>
<Link isDanger to={`/account/forget/${address}`}>Forget</Link>
{!isExternal && <Link to={`/account/export/${address}`}>Export</Link>}
</div>
<Link onClick={_toggleEdit}>Edit</Link>
</ActionBar>
</Address>
);
</div>;
}

const MenuGroup = styled.div`
padding-bottom: 16px;
margin-bottom: 16px;
border-bottom: 1px solid #222222;
`;

const MenuItem = styled(Link)`
padding: 4px 16px;
display: block;
border-radius: 8px;
font-weight: 600;
font-size: 15px;
line-height: 20px;
`;

MenuItem.displayName = 'MenuItem';

export default styled(Account)`
.edit-name {
left: 3rem;
position: absolute;
right: 0.75rem;
top: -0.5rem;
left: 73px;
top: 8px;
}
`;
11 changes: 4 additions & 7 deletions packages/extension-ui/src/Popup/Accounts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
Header,
MediaContext,
AddAccount,
ButtonArea
ButtonArea,
Svg
} from '../../components';
import Account from './Account';
import styled from 'styled-components';
Expand Down Expand Up @@ -39,13 +40,9 @@ const ButtonWithSubtitle = styled(Button)`
const QrButton = styled(Button)`
width: 60px;

span {
${Svg} {
width: 20px;
height: 20px;
display: block;
mask: url(${QrImage});
mask-size: cover;
background: ${({ theme }): string => theme.color};
}
`;

Expand Down Expand Up @@ -94,7 +91,7 @@ export default function Accounts (): React.ReactElement<Props> {
</ButtonWithSubtitle>
{mediaAllowed && (
<QrButton to='/account/import-qr'>
<span/>
<Svg src={QrImage}/>
</QrButton>
)}
</ButtonArea>
Expand Down
2 changes: 1 addition & 1 deletion packages/extension-ui/src/assets/copy.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions packages/extension-ui/src/assets/details.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/extension-ui/src/assets/print.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 12 additions & 9 deletions packages/extension-ui/src/components/ActionText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// of the Apache-2.0 license. See the LICENSE file for details.

import React, { MouseEventHandler } from 'react';
import Svg from '@polkadot/extension-ui/components/Svg';
import styled from 'styled-components';

interface Props {
Expand All @@ -15,27 +16,29 @@ interface Props {
function ActionText ({ icon, className, text, onClick }: Props): React.ReactElement<Props> {
return (
<div className={className} onClick={onClick}>
{icon && <img src={icon} alt=''/>}
{icon && <Svg src={icon}/>}
<span>{text}</span>
</div>
);
}

export default styled(ActionText)`
cursor: pointer;

span {
font-size: ${({ theme }): string => theme.labelFontSize};
line-height: ${({ theme }): string => theme.labelLineHeight};
text-decoration-line: underline;
color: ${({ theme }): string => theme.labelColor}
}

img {
${Svg} {
background: ${({ theme }): string => theme.iconLabelColor};
display: inline-block;
position: relative;
top: 2px;
width: 14px;
height: 14px;
margin-right: 6px;
}

span {
font-size: ${({ theme }): string => theme.labelFontSize};
line-height: ${({ theme }): string => theme.labelLineHeight};
text-decoration-line: underline;
color: ${({ theme }): string => theme.labelColor}
}
`;
147 changes: 114 additions & 33 deletions packages/extension-ui/src/components/Address.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,26 @@
import { AccountJson } from '@polkadot/extension/background/types';
import { Chain } from '@polkadot/extension-chains/types';

import React, { useContext, useEffect, useState } from 'react';
import React, { useContext, useEffect, useRef, useState } from 'react';
import styled from 'styled-components';
import findChain from '@polkadot/extension-chains';
import settings from '@polkadot/ui-settings';
import { decodeAddress, encodeAddress } from '@polkadot/util-crypto';

import IconBox from './IconBox';
import { AccountContext } from './contexts';
import { Identicon } from '@polkadot/extension-ui/components';
import Identicon from '@polkadot/extension-ui/components/Identicon';
import Svg from '@polkadot/extension-ui/components/Svg';
import Menu from '@polkadot/extension-ui/components/Menu';
import DetailsImg from '../assets/details.svg';
import { useOutsideClick } from '@polkadot/extension-ui/hooks';

interface Props {
address?: string | null;
children?: React.ReactNode;
className?: string;
name?: React.ReactNode | null;
children?: React.ReactNode;
genesisHash?: string | null;
actions?: React.ReactNode;
}

// find an account in our list
Expand Down Expand Up @@ -49,17 +53,19 @@ function recodeAddress (address: string, accounts: AccountJson[], genesisHash?:
];
}

function Address ({ address, children, className, genesisHash, name }: Props): React.ReactElement<Props> {
function Address ({ address, className, children, genesisHash, name, actions }: Props): React.ReactElement<Props> {
const accounts = useContext(AccountContext);
const [account, setAccount] = useState<AccountJson | null>(null);
const [chain, setChain] = useState<Chain | null>(null);
const [formatted, setFormatted] = useState<string | null>(null);
const [showActionsMenu, setShowActionsMenu] = useState(false);
const actionsRef = useRef(null);
useOutsideClick(actionsRef, () => (showActionsMenu && setShowActionsMenu(!showActionsMenu)));

useEffect((): void => {
if (!address) {
return;
}

const [formatted, account, chain] = recodeAddress(address, accounts, genesisHash);

setFormatted(formatted);
Expand All @@ -69,36 +75,111 @@ function Address ({ address, children, className, genesisHash, name }: Props): R

const theme = ((chain && chain.icon) || 'polkadot') as 'polkadot';

return (
<IconBox
banner={chain && chain.genesisHash && chain.name}
className={className}
icon={
<Identicon
iconTheme={theme}
value={address}
/>
}
intro={
<>
<div className='name'>{name || (account && account.name) || '<unknown>'}</div>
<div className='address'>{formatted || '<unknown>'}</div>
</>
}
>
{children}
</IconBox>
);
return <div className={className}>
<AccountInfoRow>
<Identicon
iconTheme={theme}
value={address}
/>
<Info>
<Name>{name || (account && account.name) || '<unknown>'}</Name>
<FullAddress>{formatted || '<unknown>'}</FullAddress>
</Info>
{actions &&
<>
<Settings onClick={(): void => setShowActionsMenu(!showActionsMenu)} ref={actionsRef}>
{showActionsMenu ? <ActiveActionsIcon/> : <ActionsIcon/>}
</Settings>
{showActionsMenu && <Menu>{actions}</Menu>}
</>}
</AccountInfoRow>
{children}
</div>;
}

export default styled(Address)`
.address {
opacity: 0.5;
overflow: hidden;
text-overflow: ellipsis;
const AccountInfoRow = styled.div`
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
height: 72px;
margin-bottom: 8px;
background: ${({ theme }): string => theme.btnAreaBackground};
`;

const Info = styled.div`
width: 100%;
`;

const Name = styled.div`
margin: 2px 0;
font-weight: 600;
font-size: 16px;
line-height: 22px;
`;

const FullAddress = styled.div`
width: 214px;
overflow: hidden;
text-overflow: ellipsis;
color: ${({ theme }): string => theme.labelColor};
font-size: 12px;
line-height: 16px;
`;

const Settings = styled.div`
position: relative;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 40px;

& ${Svg} {
width: 3px;
height: 19px;
}

&:before {
content: "";
position: absolute;
left: 0;
top: 25%;
bottom: 25%;
width: 1px;
background: ${({ theme }): string => theme.inputBorder};
}

&:hover {
cursor: pointer;
background: ${({ theme }): string => theme.readonlyInputBackground};
}
`;

Settings.displayName = 'Details';

const ActionsIcon = styled(Svg).attrs(() => ({
src: DetailsImg
}))`
background: ${({ theme }): string => theme.iconLabelColor};
`;

.name {
padding-bottom: 0.5rem;
const ActiveActionsIcon = styled(Svg).attrs(() => ({
src: DetailsImg
}))`
background: ${({ theme }): string => theme.primaryColor};
`;

export default styled(Address)`
position: relative;

& ${Identicon} {
margin-left: 25px;
margin-right: 10px;

& svg {
width: 50px;
height: 50px;
}
}
`;
Loading