diff --git a/README.md b/README.md index 8db6ca4d..e7f66924 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ As it stands, it does one thing: it _only_ manages accounts and allows the signi - On Chrome, install via [Chrome web store](https://chrome.google.com/webstore/detail/polkadot%7Bjs%7D-extension/mopnmbcafieddcagagdcbnhejhlodfdd) - On Firefox, install via [Firefox add-ons](https://addons.mozilla.org/en-US/firefox/addon/polkadot-js-extension/) -![interface screenshots](docs/concat-overview.jpg) +![interface screenshots](docs/concat-overview.png) ## FAQ diff --git a/__mocks__/fileMock.js b/__mocks__/fileMock.js new file mode 100644 index 00000000..e78939b9 --- /dev/null +++ b/__mocks__/fileMock.js @@ -0,0 +1,2 @@ +// eslint-disable-line +module.exports = ''; diff --git a/docs/concat-overview.jpg b/docs/concat-overview.jpg deleted file mode 100644 index 9b595369..00000000 Binary files a/docs/concat-overview.jpg and /dev/null differ diff --git a/docs/concat-overview.png b/docs/concat-overview.png new file mode 100644 index 00000000..090a1b8b Binary files /dev/null and b/docs/concat-overview.png differ diff --git a/jest.config.js b/jest.config.js index 473b9d1f..e6fb2a9d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,7 +6,7 @@ module.exports = Object.assign({}, config, { moduleNameMapper: { '@polkadot/extension-(chains|dapp|inject|ui)(.*)$': '/packages/extension-$1/src/$2', '@polkadot/extension(.*)$': '/packages/extension/src/$1', - '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 'empty/object', + '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '/__mocks__/fileMock.js', '\\.(css|less)$': 'empty/object' }, modulePathIgnorePatterns: [ diff --git a/packages/extension-ui/src/Popup/Accounts/Account.test.tsx b/packages/extension-ui/src/Popup/Accounts/Account.test.tsx index 7290c5f8..a71fd27f 100644 --- a/packages/extension-ui/src/Popup/Accounts/Account.test.tsx +++ b/packages/extension-ui/src/Popup/Accounts/Account.test.tsx @@ -4,11 +4,12 @@ import Adapter from 'enzyme-adapter-react-16'; import { configure, mount, ReactWrapper } from 'enzyme'; -import { Link } from '@polkadot/extension-ui/components'; +import { themes, Theme } from '@polkadot/extension-ui/components'; import { MemoryRouter } from 'react-router'; import React from 'react'; import Account from './Account'; +import { ThemeProvider } from 'styled-components'; configure({ adapter: new Adapter() }); @@ -16,27 +17,32 @@ describe('Account component', () => { let wrapper: ReactWrapper; const VALID_ADDRESS = 'HjoBp62cvsWDA3vtNMWxz6c9q13ReEHi9UGHK7JbZweH5g5'; // eslint-disable-next-line @typescript-eslint/no-explicit-any - const mountAccountComponent = (additionalAccountProperties: any): ReactWrapper => mount( + const mountAccountComponent = (additionalAccountProperties: any, theme: Theme = themes.dark): ReactWrapper => mount( - - + + + + ); 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'); }); }); diff --git a/packages/extension-ui/src/Popup/Accounts/Account.tsx b/packages/extension-ui/src/Popup/Accounts/Account.tsx index a6e4c4f4..39537487 100644 --- a/packages/extension-ui/src/Popup/Accounts/Account.tsx +++ b/packages/extension-ui/src/Popup/Accounts/Account.tsx @@ -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'; @@ -33,37 +33,62 @@ function Account ({ address, className, isExternal }: Props): React.ReactElement }; return ( -
- {isEditing && ( - - )} - -
- Forget - {!isExternal && Export} -
- Edit -
-
+
+
+ + Rename + + {!isExternal && Export Account} + Forget Account + + )} + > + {isEditing && ( + + )} +
+
); } +const MenuGroup = styled.div` + padding-bottom: 16px; + margin-bottom: 16px; + border-bottom: 1px solid ${({ theme }): string => theme.boxBorderColor}; +`; + +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)` + ${Address} { + margin-bottom: 8px; + } + .edit-name { - left: 3rem; position: absolute; - right: 0.75rem; - top: -0.5rem; + flex: 1; + left: 80px; + top: 6px; + width: 315px; } `; diff --git a/packages/extension-ui/src/Popup/Accounts/index.tsx b/packages/extension-ui/src/Popup/Accounts/index.tsx index 5ca19381..d8c9d0d5 100644 --- a/packages/extension-ui/src/Popup/Accounts/index.tsx +++ b/packages/extension-ui/src/Popup/Accounts/index.tsx @@ -3,46 +3,88 @@ // of the Apache-2.0 license. See the LICENSE file for details. import React, { useContext } from 'react'; +import QrImage from '../../assets/qr.svg'; -import { AccountContext, Button, Header, Link, MediaContext, Tip } from '../../components'; +import { + AccountContext, + Button, + Header, + MediaContext, + AddAccount, + ButtonArea, + Svg, + ButtonWithSubtitle +} from '../../components'; import Account from './Account'; +import styled from 'styled-components'; type Props = {}; export default function Accounts (): React.ReactElement { const accounts = useContext(AccountContext); const mediaAllowed = useContext(MediaContext); - return ( -
-
Options} - /> + <> { (accounts.length === 0) - ? You currently don't have any accounts. Either create a new account or if you have an existing account you wish to use, import it with the seed phrase - : accounts.map((json, index): React.ReactNode => ( - - )) + ? + : ( + <> +
+ + <> + { + accounts.map((json, index): React.ReactNode => ( + + )) + } + + + + ) } -
+ + {mediaAllowed && ( + + + + )} + + ); } + +const QrButton = styled(Button)` + width: 60px; + + ${Svg} { + width: 20px; + height: 20px; + background: ${({ theme }): string => theme.buttonTextColor}; + } +`; + +const AccountsArea = styled.div` + height: 100%; + overflow-y: scroll; + margin-top: -25px; + padding-top: 25px; + scrollbar-width: none; + + &::-webkit-scrollbar { + display: none; + } +`; diff --git a/packages/extension-ui/src/Popup/Authorize/Authorize.test.tsx b/packages/extension-ui/src/Popup/Authorize/Authorize.test.tsx new file mode 100644 index 00000000..e7a670b2 --- /dev/null +++ b/packages/extension-ui/src/Popup/Authorize/Authorize.test.tsx @@ -0,0 +1,49 @@ +// Copyright 2019 @polkadot/extension-ui authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import React from 'react'; +import Adapter from 'enzyme-adapter-react-16'; + +import { ThemeProvider } from 'styled-components'; +import { AuthorizeContext, themes, Header, Icon } from '@polkadot/extension-ui/components'; +import Authorize from '.'; +import { configure, mount, ReactWrapper } from 'enzyme'; +import { AuthorizeRequest } from '@polkadot/extension/background/types'; +import Request from './Request'; + +configure({ adapter: new Adapter() }); + +describe('Authorize', () => { + const mountAuthorize = (authorizeRequests: AuthorizeRequest[] = []): ReactWrapper => mount( + + + + + ); + + it('render component', () => { + const wrapper = mountAuthorize(); + + expect(wrapper.find(Header).text()).toBe('Authorize'); + expect(wrapper.find(Request).length).toBe(0); + }); + + it('render requests', () => { + const wrapper = mountAuthorize([{ id: '1', request: { origin: '???' }, url: 'http://polkadot.org' }]); + + expect(wrapper.find(Request).length).toBe(1); + expect(wrapper.find(Request).find('.tab-info').text()).toBe('An application, self-identifying as ??? is requesting access from http://polkadot.org.'); + }); + + it('render more request but just one accept button', () => { + const wrapper = mountAuthorize([ + { id: '1', request: { origin: '???' }, url: 'http://polkadot.org' }, + { id: '2', request: { origin: 'abc' }, url: 'http://polkadot.pl' } + ]); + + expect(wrapper.find('AcceptButton').length).toBe(1); + expect(wrapper.find(Icon).length).toBe(2); + expect(wrapper.find(Request).at(1).find('.tab-info').text()).toBe('An application, self-identifying as abc is requesting access from http://polkadot.pl.'); + }); +}); diff --git a/packages/extension-ui/src/Popup/Authorize/Request.tsx b/packages/extension-ui/src/Popup/Authorize/Request.tsx index 5d70a48a..90495108 100644 --- a/packages/extension-ui/src/Popup/Authorize/Request.tsx +++ b/packages/extension-ui/src/Popup/Authorize/Request.tsx @@ -7,7 +7,7 @@ import { RequestAuthorizeTab } from '@polkadot/extension/background/types'; import React, { useContext } from 'react'; import styled from 'styled-components'; -import { ActionBar, ActionContext, Button, Icon, IconBox, Link, Tip, defaults } from '../../components'; +import { ActionBar, ActionContext, Button, Icon, Link, Warning } from '../../components'; import { approveAuthRequest, rejectAuthRequest } from '../../messaging'; interface Props { @@ -30,52 +30,93 @@ function Request ({ authId, className, isFirst, request: { origin }, url }: Prop .catch((error: Error) => console.error(error)); return ( - - } - intro={ -
An application, self-identifying as {origin} is requesting access from {url}.
- } - > - -
- Reject - - {isFirst && ( - <> - Only approve this request if you trust the application. Approving gives the application access to the addresses of your accounts. -