From e7fb3651ab72df383068eb2526cf3062a118983a Mon Sep 17 00:00:00 2001 From: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Date: Fri, 23 Oct 2020 20:26:14 +0200 Subject: [PATCH] Style component global cleanup (#518) * ButtonArea * List * Box and Welcome screen * Svg * remove single used Title * ValidateInput * VerticalSpace * View * Export * Forget * DropdownAddress * Accounts * Request * Authorize * SelectParent * Header * Menu item spacing * fix tests * Apply suggestions from code review Co-authored-by: Jaco Greeff * Refactor Request into smaller components (#519) * refactor into smaller components * stray component * Update packages/extension-ui/src/Popup/Signing/Request/SignArea.tsx Co-authored-by: Jaco Greeff * lint Co-authored-by: Github Actions Co-authored-by: Jaco Greeff Co-authored-by: Github Actions Co-authored-by: Jaco Greeff --- .../extension-ui/src/Popup/Accounts/index.tsx | 14 +- .../src/Popup/Authorize/Authorize.test.tsx | 3 +- .../src/Popup/Authorize/Request.tsx | 87 +++--- .../src/Popup/Authorize/index.tsx | 23 +- .../CreateAccount/CreateAccount.test.tsx | 10 +- .../src/Popup/Derive/AddressDropdown.tsx | 55 ++-- .../src/Popup/Derive/SelectParent.tsx | 120 ++++---- .../extension-ui/src/Popup/Derive/index.tsx | 2 +- packages/extension-ui/src/Popup/Export.tsx | 53 ++-- packages/extension-ui/src/Popup/Forget.tsx | 51 ++-- .../extension-ui/src/Popup/ImportSeed.tsx | 7 +- .../src/Popup/Signing/Request.tsx | 259 ------------------ .../src/Popup/Signing/Request/SignArea.tsx | 142 ++++++++++ .../src/Popup/Signing/Request/index.tsx | 141 ++++++++++ packages/extension-ui/src/Popup/Welcome.tsx | 46 ++-- .../extension-ui/src/components/Address.tsx | 1 + packages/extension-ui/src/components/Box.tsx | 6 +- .../src/components/ButtonArea.tsx | 26 +- packages/extension-ui/src/components/List.tsx | 15 +- packages/extension-ui/src/components/Main.tsx | 40 +++ .../extension-ui/src/components/MenuItem.tsx | 26 +- packages/extension-ui/src/components/Svg.tsx | 9 +- packages/extension-ui/src/components/Title.ts | 19 -- .../src/components/ValidatedInput.tsx | 22 +- .../src/components/VerticalSpace.ts | 8 - .../src/components/VerticalSpace.tsx | 18 ++ packages/extension-ui/src/components/View.tsx | 24 +- packages/extension-ui/src/components/index.ts | 2 +- packages/extension-ui/src/partials/Header.tsx | 8 +- 29 files changed, 682 insertions(+), 555 deletions(-) delete mode 100644 packages/extension-ui/src/Popup/Signing/Request.tsx create mode 100644 packages/extension-ui/src/Popup/Signing/Request/SignArea.tsx create mode 100644 packages/extension-ui/src/Popup/Signing/Request/index.tsx create mode 100644 packages/extension-ui/src/components/Main.tsx delete mode 100644 packages/extension-ui/src/components/Title.ts delete mode 100644 packages/extension-ui/src/components/VerticalSpace.ts create mode 100644 packages/extension-ui/src/components/VerticalSpace.tsx diff --git a/packages/extension-ui/src/Popup/Accounts/index.tsx b/packages/extension-ui/src/Popup/Accounts/index.tsx index 7cd16a64..749a0172 100644 --- a/packages/extension-ui/src/Popup/Accounts/index.tsx +++ b/packages/extension-ui/src/Popup/Accounts/index.tsx @@ -1,6 +1,8 @@ // Copyright 2019-2020 @polkadot/extension-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../../types'; + import React, { useContext } from 'react'; import styled from 'styled-components'; @@ -10,7 +12,11 @@ import { Header } from '../../partials'; import AccountsTree from './AccountsTree'; import AddAccount from './AddAccount'; -export default function Accounts (): React.ReactElement { +interface Props extends ThemeProps { + className?: string; +} + +function Accounts ({ className }: Props): React.ReactElement { const { t } = useTranslation(); const { hierarchy } = useContext(AccountContext); @@ -25,14 +31,14 @@ export default function Accounts (): React.ReactElement { showSettings text={t('Accounts')} /> - +
{hierarchy.map((json, index): React.ReactNode => ( ))} - +
) } @@ -40,7 +46,7 @@ export default function Accounts (): React.ReactElement { ); } -const AccountsArea = styled.div` +export default styled(Accounts)` height: 100%; overflow-y: scroll; margin-top: -25px; diff --git a/packages/extension-ui/src/Popup/Authorize/Authorize.test.tsx b/packages/extension-ui/src/Popup/Authorize/Authorize.test.tsx index ff2a23e8..9eb079d7 100644 --- a/packages/extension-ui/src/Popup/Authorize/Authorize.test.tsx +++ b/packages/extension-ui/src/Popup/Authorize/Authorize.test.tsx @@ -47,8 +47,9 @@ describe('Authorize', () => { { id: '2', request: { origin: 'abc' }, url: 'http://polkadot.pl' } ]); - expect(wrapper.find('AcceptButton').length).toBe(1); + expect(wrapper.find(Request).length).toBe(2); 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.'); + expect(wrapper.find('button.acceptButton').length).toBe(1); }); }); diff --git a/packages/extension-ui/src/Popup/Authorize/Request.tsx b/packages/extension-ui/src/Popup/Authorize/Request.tsx index 20ab44a8..3602e49b 100644 --- a/packages/extension-ui/src/Popup/Authorize/Request.tsx +++ b/packages/extension-ui/src/Popup/Authorize/Request.tsx @@ -12,7 +12,7 @@ import { ActionBar, ActionContext, Button, Icon, Link, Warning } from '../../com import useTranslation from '../../hooks/useTranslation'; import { approveAuthRequest, rejectAuthRequest } from '../../messaging'; -interface Props { +interface Props extends ThemeProps { authId: string; className?: string; isFirst: boolean; @@ -40,8 +40,8 @@ function Request ({ authId, className, isFirst, request: { origin }, url }: Prop return (
- - +
+
.
- +
{isFirst && ( <> - {t('Only approve this request if you trust the application. Approving gives the application access to the addresses of your accounts.')} - {t('Yes, allow this application access')} + {t('Only approve this request if you trust the application. Approving gives the application access to the addresses of your accounts.')} + )} - + Reject - -
+ +
); } -const RequestInfo = styled.div` - display: flex; - flex-direction: column; - align-items: center; - margin-bottom: 8px; - background: ${({ theme }: ThemeProps): string => theme.highlightedAreaBackground}; -`; - -const Info = styled.div` - display: flex; - flex-direction: row; -`; - -const AcceptButton = styled(Button)` - width: 90%; - margin: 25px auto 0; -`; - -const RequestWarning = styled(Warning)` - margin: 24px 24px 0 1.45rem; -`; - -AcceptButton.displayName = 'AcceptButton'; - -const RejectButton = styled(ActionBar)` - margin: 8px 0 15px 0; - text-decoration: underline; -`; - -export default styled(Request)` +export default styled(Request)(({ theme }: Props) => ` .icon { - background: ${({ theme }: ThemeProps): string => theme.buttonBackgroundDanger}; + background: ${theme.buttonBackgroundDanger}; color: white; min-width: 18px; width: 14px; @@ -128,7 +104,7 @@ export default styled(Request)` .tab-name, .tab-url { - color: ${({ theme }: ThemeProps): string => theme.textColor}; + color: ${theme.textColor}; display: inline-block; max-width: 20rem; overflow: hidden; @@ -137,4 +113,31 @@ export default styled(Request)` cursor: pointer; text-decoration: underline; } -`; + + .requestInfo { + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 8px; + background: ${theme.highlightedAreaBackground}; + } + + .info { + display: flex; + flex-direction: row; + } + + .acceptButton { + width: 90%; + margin: 25px auto 0; + } + + .warningMargin { + margin: 24px 24px 0 1.45rem; + } + + .rejectionButton { + margin: 8px 0 15px 0; + text-decoration: underline; + } +`); diff --git a/packages/extension-ui/src/Popup/Authorize/index.tsx b/packages/extension-ui/src/Popup/Authorize/index.tsx index d8219142..abf43cfe 100644 --- a/packages/extension-ui/src/Popup/Authorize/index.tsx +++ b/packages/extension-ui/src/Popup/Authorize/index.tsx @@ -1,6 +1,8 @@ // Copyright 2019-2020 @polkadot/extension-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../../types'; + import React, { useContext } from 'react'; import styled from 'styled-components'; @@ -9,36 +11,45 @@ import useTranslation from '../../hooks/useTranslation'; import { Header } from '../../partials'; import Request from './Request'; -export default function Authorize (): React.ReactElement { +interface Props extends ThemeProps { + className?: string; +} + +function Authorize ({ className = '' }: Props): React.ReactElement { const { t } = useTranslation(); const requests = useContext(AuthorizeReqContext); return ( <> - +
('Authorize')} /> {requests.map(({ id, request, url }, index): React.ReactNode => ( ))} - +
); } -const Scroll = styled.div<{isLastRequest: boolean}>` - overflow-y: ${({ isLastRequest }): string => isLastRequest ? 'hidden' : 'auto'}; +export default styled(Authorize)` + overflow-y: auto; + + &.lastRequest { + overflow: hidden; + } && { padding: 0; } - ${Request} { + .request { padding: 0 24px; } `; diff --git a/packages/extension-ui/src/Popup/CreateAccount/CreateAccount.test.tsx b/packages/extension-ui/src/Popup/CreateAccount/CreateAccount.test.tsx index 472a75b6..ce216789 100644 --- a/packages/extension-ui/src/Popup/CreateAccount/CreateAccount.test.tsx +++ b/packages/extension-ui/src/Popup/CreateAccount/CreateAccount.test.tsx @@ -101,7 +101,7 @@ describe('Create Account', () => { it('after typing less than 3 characters into name input, password input is not visible', async () => { await enterName('ab'); expect(wrapper.find(InputWithLabel).find('[data-input-name]').find(Input).prop('withError')).toBe(true); - expect(wrapper.find('ErrorMessage').first().text()).toBe('Account name is too short'); + expect(wrapper.find('span.error').first().text()).toBe('Account name is too short'); expect(wrapper.find(InputWithLabel).find('[data-input-password]').find(Input)).toHaveLength(1); expect(wrapper.find(InputWithLabel).find('[data-input-repeat-password]')).toHaveLength(0); expect(wrapper.find('[data-button-action="add new root"] button').prop('disabled')).toBe(true); @@ -124,7 +124,7 @@ describe('Create Account', () => { it('password shorter than 6 characters should be not valid', async () => { await enterName('abc').then(password('abcde')); expect(wrapper.find(InputWithLabel).find('[data-input-password]').find(Input).prop('withError')).toBe(true); - expect(wrapper.find('ErrorMessage').text()).toBe('Password is too short'); + expect(wrapper.find('span.error').text()).toBe('Password is too short'); expect(wrapper.find(InputWithLabel).find('[data-input-password]').find(Input)).toHaveLength(1); expect(wrapper.find(InputWithLabel).find('[data-input-repeat-password]')).toHaveLength(0); expect(wrapper.find('[data-button-action="add new root"] button').prop('disabled')).toBe(true); @@ -132,14 +132,14 @@ describe('Create Account', () => { it('submit button is not visible until both passwords are equal', async () => { await enterName('abc').then(password('abcdef')).then(repeat('abcdeg')); - expect(wrapper.find('ErrorMessage').text()).toBe('Passwords do not match'); + expect(wrapper.find('span.error').text()).toBe('Passwords do not match'); expect(wrapper.find(InputWithLabel).find('[data-input-repeat-password]').find(Input).prop('withError')).toBe(true); expect(wrapper.find('[data-button-action="add new root"] button').prop('disabled')).toBe(true); }); it('submit button is visible when both passwords are equal', async () => { await enterName('abc').then(password('abcdef')).then(repeat('abcdef')); - expect(wrapper.find('ErrorMessage')).toHaveLength(0); + expect(wrapper.find('span.error')).toHaveLength(0); expect(wrapper.find(InputWithLabel).find('[data-input-repeat-password]').find(Input).prop('withError')).toBe(false); expect(wrapper.find('[data-button-action="add new root"] button').prop('disabled')).toBe(false); }); @@ -170,7 +170,7 @@ describe('Create Account', () => { it('first password changes - button is not disabled', async () => { await type(wrapper.find('input[type="password"]').first(), 'aaaaaa'); - expect(wrapper.find('ErrorMessage').text()).toBe('Passwords do not match'); + expect(wrapper.find('span.error').text()).toBe('Passwords do not match'); expect(wrapper.find('[data-button-action="add new root"] button').prop('disabled')).toBe(true); }); diff --git a/packages/extension-ui/src/Popup/Derive/AddressDropdown.tsx b/packages/extension-ui/src/Popup/Derive/AddressDropdown.tsx index 91ddabd7..5399886a 100644 --- a/packages/extension-ui/src/Popup/Derive/AddressDropdown.tsx +++ b/packages/extension-ui/src/Popup/Derive/AddressDropdown.tsx @@ -7,7 +7,7 @@ import React, { useCallback, useRef, useState } from 'react'; import styled from 'styled-components'; import arrow from '../../assets/arrow-down.svg'; -import { Address, Svg } from '../../components'; +import { Address } from '../../components'; import useOutsideClick from '../../hooks/useOutsideClick'; interface Props { @@ -36,10 +36,11 @@ function AddressDropdown ({ allAddresses, className, onSelect, selectedAddress, >
- +
{allAddresses.map(([address, genesisHash]) => (
))} - +
); } -const HiddenOptions = styled.div<{ visible: boolean }>` - position: absolute; - visibility: ${({ visible }): string => (visible ? 'visible' : 'hidden')}; - width: 510px; - z-index: 100; - background: ${({ theme }: ThemeProps): string => theme.bodyColor}; - max-height: ${({ visible }): string => (visible ? '200px' : '0')}; - overflow: scroll; - padding: 5px; - border: 1px solid ${({ theme }: ThemeProps): string => theme.boxBorderColor}; - box-sizing: border-box; - border-radius: 4px; - margin-top: -8px; - - & > div { - cursor: pointer; - } -`; - export default styled(AddressDropdown)(({ theme }: ThemeProps) => ` margin-bottom: 16px; cursor: pointer; - & > div:first-child > ${Address}::after { + & > div:first-child > .address::after { content: ''; position: absolute; top: 66%; @@ -95,7 +78,31 @@ export default styled(AddressDropdown)(({ theme }: ThemeProps) => ` border: 1px solid ${theme.boxBorderColor}; } - ${Address} ${Svg} { + .address .copyIcon { visibility: hidden; } + + .dropdown { + position: absolute; + visibility: hidden; + width: 510px; + z-index: 100; + background: ${theme.bodyColor}; + max-height: 0; + overflow: auto; + padding: 5px; + border: 1px solid ${theme.boxBorderColor}; + box-sizing: border-box; + border-radius: 4px; + margin-top: -8px; + + &.visible{ + visibility: visible; + max-height: 200px; + } + + & > div { + cursor: pointer; + } + } `); diff --git a/packages/extension-ui/src/Popup/Derive/SelectParent.tsx b/packages/extension-ui/src/Popup/Derive/SelectParent.tsx index 2a6b14b0..c4d5c841 100644 --- a/packages/extension-ui/src/Popup/Derive/SelectParent.tsx +++ b/packages/extension-ui/src/Popup/Derive/SelectParent.tsx @@ -1,6 +1,8 @@ // Copyright 2019-2020 @polkadot/extension-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../../types'; + import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; import styled from 'styled-components'; import { assert } from '@polkadot/util'; @@ -12,14 +14,15 @@ import { nextDerivationPath } from '../../util/nextDerivationPath'; import AddressDropdown from './AddressDropdown'; import DerivationPath from './DerivationPath'; -interface Props { +interface Props extends ThemeProps { + className?: string; isLocked?: boolean; parentAddress: string; parentGenesis: string | null; onDerivationConfirmed: (derivation: { account: { address: string; suri: string }; parentPassword: string }) => void; } -export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress, parentGenesis }: Props): React.ReactElement { +function SelectParent ({ className, isLocked, onDerivationConfirmed, parentAddress, parentGenesis }: Props): React.ReactElement { const { t } = useTranslation(); const onAction = useContext(ActionContext); const [isBusy, setIsBusy] = useState(false); @@ -84,58 +87,60 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress, setParentPassword(''); setIsProperParentPassword(false); - // eslint-disable-next-line no-unused-expressions passwordInputRef.current?.querySelector('input')?.focus(); }, [_onParentPasswordEnter]); return ( <> - {!isLocked && ( - ('Derive new account from existing')} - onChange={setShouldAccountBeDerived} - /> - )} - - {isLocked - ? ( -
- ) - : ( - - ) - } -
- ('enter the password for the account you want to derive from')} - onChange={_onParentPasswordEnter} - type='password' - value={parentPassword} - /> -
- {isProperParentPassword && ( - + {!isLocked && ( + ('Derive new account from existing')} + onChange={setShouldAccountBeDerived} /> )} - +
+ {isLocked + ? ( +
+ ) + : ( + + ) + } +
+ ('enter the password for the account you want to derive from')} + onChange={_onParentPasswordEnter} + type='password' + value={parentPassword} + /> +
+ {isProperParentPassword && ( + + )} +
+ {shouldAccountBeDerived @@ -163,12 +168,19 @@ export function SelectParent ({ isLocked, onDerivationConfirmed, parentAddress, ); } -const CheckboxWithSmallerMargins = styled(Checkbox)` - margin-top: 0; - margin-bottom: 10px; -`; +export default styled(SelectParent)` -const DisableableArea = styled.div<{ isDisabled: boolean }>` - opacity: ${({ isDisabled }): string => isDisabled ? '0.2' : '1'}; - ${({ isDisabled }): string | false => isDisabled && 'pointer-events: none'}; + .smallerMargin { + margin-top: 0; + margin-bottom: 10px; + } + + .disableArea { + opacity: 1; + + .disabled { + opacity: 0.2; + pointer-events: none; + } + } `; diff --git a/packages/extension-ui/src/Popup/Derive/index.tsx b/packages/extension-ui/src/Popup/Derive/index.tsx index 70237643..8f3061c9 100644 --- a/packages/extension-ui/src/Popup/Derive/index.tsx +++ b/packages/extension-ui/src/Popup/Derive/index.tsx @@ -8,7 +8,7 @@ import { AccountContext, ActionContext, Address, BackButton, ButtonArea, NextSte import useTranslation from '../../hooks/useTranslation'; import { deriveAccount } from '../../messaging'; import { HeaderWithSteps, Name, Password } from '../../partials'; -import { SelectParent } from './SelectParent'; +import SelectParent from './SelectParent'; interface Props { isLocked?: boolean; diff --git a/packages/extension-ui/src/Popup/Export.tsx b/packages/extension-ui/src/Popup/Export.tsx index fe7dc2b0..20bae1fa 100644 --- a/packages/extension-ui/src/Popup/Export.tsx +++ b/packages/extension-ui/src/Popup/Export.tsx @@ -1,6 +1,8 @@ // Copyright 2019-2020 @polkadot/extension-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../types'; + import React, { useCallback, useContext, useState } from 'react'; import { RouteComponentProps, withRouter } from 'react-router'; @@ -12,9 +14,11 @@ import styled from 'styled-components'; const MIN_LENGTH = 6; -type Props = RouteComponentProps<{ address: string }>; +interface Props extends RouteComponentProps<{address: string}>, ThemeProps { + className?: string; +} -function Export ({ match: { params: { address } } }: Props): React.ReactElement { +function Export ({ className, match: { params: { address } } }: Props): React.ReactElement { const { t } = useTranslation(); const onAction = useContext(ActionContext); const [isBusy, setIsBusy] = useState(false); @@ -58,10 +62,15 @@ function Export ({ match: { params: { address } } }: Props): React.ReactElement< showBackArrow text={t('Export account')} /> -
+
- {t("You are exporting your account. Keep it safe and don't share it with anyone.")} - + + {t("You are exporting your account. Keep it safe and don't share it with anyone.")} + +
{t('I want to export this account')} - + ('Cancel')} /> - - + +
); } -const MovedWarning = styled(Warning)` - margin-top: 8px; -`; +export default withRouter(styled(Export)` + .actionArea { + padding: 10px 24px; + } -const ActionArea = styled.div` - padding: 10px 24px; -`; - -const CancelButton = styled(ActionBar)` - margin-top: 4px; - text-decoration: underline; - - ${ActionText} { + .center { margin: auto; } -`; -export default withRouter(Export); + .movedWarning { + margin-top: 8px; + } + + .withMarginTop { + margin-top: 4px; + } +`); diff --git a/packages/extension-ui/src/Popup/Forget.tsx b/packages/extension-ui/src/Popup/Forget.tsx index b7c799b0..87f1d9ad 100644 --- a/packages/extension-ui/src/Popup/Forget.tsx +++ b/packages/extension-ui/src/Popup/Forget.tsx @@ -1,5 +1,6 @@ // Copyright 2019-2020 @polkadot/extension-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../types'; import React, { useCallback, useContext } from 'react'; import { RouteComponentProps, withRouter } from 'react-router'; @@ -10,9 +11,11 @@ import useTranslation from '../hooks/useTranslation'; import { forgetAccount } from '../messaging'; import { Header } from '../partials'; -type Props = RouteComponentProps<{ address: string }>; +interface Props extends RouteComponentProps<{ address: string }>, ThemeProps { + className?: string; +} -function Forget ({ match: { params: { address } } }: Props): React.ReactElement { +function Forget ({ className, match: { params: { address } } }: Props): React.ReactElement { const { t } = useTranslation(); const onAction = useContext(ActionContext); @@ -35,43 +38,49 @@ function Forget ({ match: { params: { address } } }: Props): React.ReactElement< showBackArrow text={t('Forget account')} /> -
+
- {t('You are about to remove the account. This means that you will not be able to access it via this extension anymore. If you wish to recover it, you would need to use the seed.')} - + + {t('You are about to remove the account. This means that you will not be able to access it via this extension anymore. If you wish to recover it, you would need to use the seed.')} + +
- + ('Cancel')} /> - - + +
); } -const MovedWarning = styled(Warning)` - margin-top: 8px; -`; +export default withRouter(styled(Forget)` + .actionArea { + padding: 10px 24px; + } -const ActionArea = styled.div` - padding: 10px 24px; -`; - -const CancelButton = styled(ActionBar)` - margin-top: 4px; - - ${ActionText} { + .center { margin: auto; } -`; -export default withRouter(Forget); + .movedWarning { + margin-top: 8px; + } + + .withMarginTop { + margin-top: 4px; + } +`); diff --git a/packages/extension-ui/src/Popup/ImportSeed.tsx b/packages/extension-ui/src/Popup/ImportSeed.tsx index e58f616b..0b0f04bb 100644 --- a/packages/extension-ui/src/Popup/ImportSeed.tsx +++ b/packages/extension-ui/src/Popup/ImportSeed.tsx @@ -70,8 +70,9 @@ export default function Import (): React.ReactElement { return ( <> - ('Import account')} />
@@ -112,10 +113,6 @@ export default function Import (): React.ReactElement { ); } -const HeaderWithSmallerMargin = styled(Header)` - margin-bottom: 15px; -`; - const SeedInput = styled(TextAreaWithLabel)` margin-bottom: 16px; textarea { diff --git a/packages/extension-ui/src/Popup/Signing/Request.tsx b/packages/extension-ui/src/Popup/Signing/Request.tsx deleted file mode 100644 index 7daf94b9..00000000 --- a/packages/extension-ui/src/Popup/Signing/Request.tsx +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2019-2020 @polkadot/extension-ui authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -import { ExtrinsicPayload } from '@polkadot/types/interfaces'; -import { AccountJson, RequestSign } from '@polkadot/extension-base/background/types'; -import { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; - -import React, { useCallback, useEffect, useContext, useState } from 'react'; -import { PASSWORD_EXPIRY_MIN } from '@polkadot/extension-base/defaults'; -import { TypeRegistry } from '@polkadot/types'; - -import { ActionBar, ActionContext, Address, Button, ButtonArea, Checkbox, Link, VerticalSpace } from '../../components'; -import useTranslation from '../../hooks/useTranslation'; -import { approveSignPassword, approveSignSignature, cancelSignRequest, isSignLocked } from '../../messaging'; -import Bytes from './Bytes'; -import Extrinsic from './Extrinsic'; -import Qr from './Qr'; -import Unlock from './Unlock'; -import styled from 'styled-components'; - -interface Props { - account: AccountJson; - buttonText: string; - isFirst?: boolean; - request: RequestSign; - signId: string; - url: string; -} - -interface Data { - hexBytes: string | null; - payload: ExtrinsicPayload | null; -} - -// keep it global, we can and will re-use this across requests -const registry = new TypeRegistry(); - -function isRawPayload (payload: SignerPayloadJSON | SignerPayloadRaw): payload is SignerPayloadRaw { - return !!(payload as SignerPayloadRaw).data; -} - -export default function Request ({ account: { isExternal }, buttonText, isFirst, request, signId, url }: Props): React.ReactElement | null { - const { t } = useTranslation(); - const onAction = useContext(ActionContext); - const [{ hexBytes, payload }, setData] = useState({ hexBytes: null, payload: null }); - const [error, setError] = useState(null); - const [isBusy, setIsBusy] = useState(false); - const [isLocked, setIsLocked] = useState(null); - const [savePass, setSavePass] = useState(false); - const [password, setPassword] = useState(''); - - useEffect(() => { - setIsLocked(null); - let timeout: number; - - !isExternal && isSignLocked(signId) - .then(({ isLocked, remainingTime }) => { - setIsLocked(isLocked); - timeout = setTimeout(() => { - setIsLocked(true); - }, remainingTime); - - // if the account was unlocked check the remember me - // automatically to prolong the unlock period - !isLocked && setSavePass(true); - }) - .catch((error: Error) => console.error(error)); - - return () => { !!timeout && clearTimeout(timeout); }; - }, [isExternal, signId]); - - useEffect((): void => { - const payload = request.payload; - - if (isRawPayload(payload)) { - setData({ - hexBytes: payload.data, - payload: null - }); - } else { - registry.setSignedExtensions(payload.signedExtensions); - - setData({ - hexBytes: null, - payload: registry.createType('ExtrinsicPayload', payload, { version: payload.version }) - }); - } - }, [request]); - - const _onCancel = useCallback( - (): Promise => cancelSignRequest(signId) - .then(() => onAction()) - .catch((error: Error) => console.error(error)), - [onAction, signId] - ); - - const _onSign = useCallback( - (): Promise => { - setIsBusy(true); - - return approveSignPassword(signId, savePass, password) - .then((): void => { - setIsBusy(false); - onAction(); - }) - .catch((error: Error): void => { - setIsBusy(false); - setError(error.message); - console.error(error); - }); - }, - [onAction, password, savePass, signId] - ); - - const _onSignature = useCallback( - ({ signature }: { signature: string }): Promise => - approveSignSignature(signId, signature) - .then(() => onAction()) - .catch((error: Error): void => { - setError(error.message); - console.error(error); - }), - [onAction, signId] - ); - - const RememberPasswordCheckbox = () => ( - ( - 'Remember my password for the next {{expiration}} minutes', - { replace: { expiration: PASSWORD_EXPIRY_MIN } } - ) - : t( - 'Extend the period without password by {{expiration}} minutes', - { replace: { expiration: PASSWORD_EXPIRY_MIN } } - ) - } - onChange={setSavePass} - /> - ); - - const SignButton = () => ( - <> - { isLocked && ( - - )} - - - - ); - - if (payload !== null) { - const json = request.payload as SignerPayloadJSON; - - return ( - <> -
-
-
- {isExternal - ? ( - - ) - : ( - - ) - } - - {isFirst && !isExternal && ( - - )} - - - {t('Cancel')} - - - - - ); - } else if (hexBytes !== null) { - const raw = request.payload as SignerPayloadRaw; - - return ( - <> -
-
-
- - - - {!isExternal && } - - - {t('Reject')} - - - - - ); - } - - return null; -} - -const SignArea = styled(ButtonArea)` - flex-direction: column; - padding: 6px 24px; -`; - -const CancelButton = styled(ActionBar)` - margin-top: 4px; - margin-bottom: 4px; - text-decoration: underline; - - a { - margin: auto; - } -`; - -CancelButton.displayName = 'CancelButton'; diff --git a/packages/extension-ui/src/Popup/Signing/Request/SignArea.tsx b/packages/extension-ui/src/Popup/Signing/Request/SignArea.tsx new file mode 100644 index 00000000..16107495 --- /dev/null +++ b/packages/extension-ui/src/Popup/Signing/Request/SignArea.tsx @@ -0,0 +1,142 @@ +// Copyright 2019-2020 @polkadot/extension-ui authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { PASSWORD_EXPIRY_MIN } from '@polkadot/extension-base/defaults'; +import React, { useCallback, useContext, useEffect, useState } from 'react'; +import styled from 'styled-components'; + +import { ActionBar, ActionContext, Button, ButtonArea, Checkbox, Link } from '../../../components'; +import useTranslation from '../../../hooks/useTranslation'; +import { approveSignPassword, cancelSignRequest, isSignLocked } from '../../../messaging'; +import Unlock from '../Unlock'; + +interface Props { + buttonText: string; + className?: string; + error: string | null; + isExternal?: boolean; + isFirst: boolean; + setError: (value: string | null) => void; + signId: string; +} + +function SignArea ({ buttonText, className, error, isExternal, isFirst, setError, signId } : Props): JSX.Element { + const [savePass, setSavePass] = useState(false); + const [isLocked, setIsLocked] = useState(null); + const [password, setPassword] = useState(''); + const [isBusy, setIsBusy] = useState(false); + const onAction = useContext(ActionContext); + const { t } = useTranslation(); + + useEffect(() => { + setIsLocked(null); + let timeout: number; + + !isExternal && isSignLocked(signId) + .then(({ isLocked, remainingTime }) => { + setIsLocked(isLocked); + timeout = setTimeout(() => { + setIsLocked(true); + }, remainingTime); + + // if the account was unlocked check the remember me + // automatically to prolong the unlock period + !isLocked && setSavePass(true); + }) + .catch((error: Error) => console.error(error)); + + return () => { !!timeout && clearTimeout(timeout); }; + }, [isExternal, signId]); + + const _onSign = useCallback( + (): Promise => { + setIsBusy(true); + + return approveSignPassword(signId, savePass, password) + .then((): void => { + setIsBusy(false); + onAction(); + }) + .catch((error: Error): void => { + setIsBusy(false); + setError(error.message); + console.error(error); + }); + }, + [onAction, password, savePass, setError, setIsBusy, signId] + ); + + const _onCancel = useCallback( + (): Promise => cancelSignRequest(signId) + .then(() => onAction()) + .catch((error: Error) => console.error(error)), + [onAction, signId] + ); + + const RememberPasswordCheckbox = () => ( + ( + 'Remember my password for the next {{expiration}} minutes', + { replace: { expiration: PASSWORD_EXPIRY_MIN } } + ) + : t( + 'Extend the period without password by {{expiration}} minutes', + { replace: { expiration: PASSWORD_EXPIRY_MIN } } + ) + } + onChange={setSavePass} + /> + ); + + return ( + + {isFirst && !isExternal && ( + <> + { isLocked && ( + + )} + + + + )} + + + {t('Cancel')} + + + + ); +} + +export default styled(SignArea)` + flex-direction: column; + padding: 6px 24px; + + .cancelButton { + margin-top: 4px; + margin-bottom: 4px; + text-decoration: underline; + + a { + margin: auto; + } + } +`; diff --git a/packages/extension-ui/src/Popup/Signing/Request/index.tsx b/packages/extension-ui/src/Popup/Signing/Request/index.tsx new file mode 100644 index 00000000..46d549dd --- /dev/null +++ b/packages/extension-ui/src/Popup/Signing/Request/index.tsx @@ -0,0 +1,141 @@ +// Copyright 2019-2020 @polkadot/extension-ui authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { ExtrinsicPayload } from '@polkadot/types/interfaces'; +import { AccountJson, RequestSign } from '@polkadot/extension-base/background/types'; +import { TypeRegistry } from '@polkadot/types'; +import { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; + +import React, { useCallback, useEffect, useContext, useState } from 'react'; + +import { ActionContext, Address, VerticalSpace } from '../../../components'; +import { approveSignSignature } from '../../../messaging'; +import Bytes from '../Bytes'; +import Extrinsic from '../Extrinsic'; +import Qr from '../Qr'; +import SignArea from './SignArea'; + +interface Props { + account: AccountJson; + buttonText: string; + isFirst: boolean; + request: RequestSign; + signId: string; + url: string; +} + +interface Data { + hexBytes: string | null; + payload: ExtrinsicPayload | null; +} + +// keep it global, we can and will re-use this across requests +const registry = new TypeRegistry(); + +function isRawPayload (payload: SignerPayloadJSON | SignerPayloadRaw): payload is SignerPayloadRaw { + return !!(payload as SignerPayloadRaw).data; +} + +export default function Request ({ account: { isExternal }, buttonText, isFirst, request, signId, url }: Props): React.ReactElement | null { + const onAction = useContext(ActionContext); + const [{ hexBytes, payload }, setData] = useState({ hexBytes: null, payload: null }); + const [error, setError] = useState(null); + + useEffect((): void => { + const payload = request.payload; + + if (isRawPayload(payload)) { + setData({ + hexBytes: payload.data, + payload: null + }); + } else { + registry.setSignedExtensions(payload.signedExtensions); + + setData({ + hexBytes: null, + payload: registry.createType('ExtrinsicPayload', payload, { version: payload.version }) + }); + } + }, [request]); + + const _onSignature = useCallback( + ({ signature }: { signature: string }): Promise => + approveSignSignature(signId, signature) + .then(() => onAction()) + .catch((error: Error): void => { + setError(error.message); + console.error(error); + }), + [onAction, signId] + ); + + if (payload !== null) { + const json = request.payload as SignerPayloadJSON; + + return ( + <> +
+
+
+ {isExternal + ? ( + + ) + : ( + + ) + } + + + ); + } else if (hexBytes !== null) { + const raw = request.payload as SignerPayloadRaw; + + return ( + <> +
+
+
+ + + + + ); + } + + return null; +} diff --git a/packages/extension-ui/src/Popup/Welcome.tsx b/packages/extension-ui/src/Popup/Welcome.tsx index 99d1fee3..e884960d 100644 --- a/packages/extension-ui/src/Popup/Welcome.tsx +++ b/packages/extension-ui/src/Popup/Welcome.tsx @@ -1,7 +1,7 @@ // Copyright 2019-2020 @polkadot/extension-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { ThemeProps as Props } from '../types'; +import { ThemeProps } from '../types'; import React, { useContext } from 'react'; import styled from 'styled-components'; @@ -10,7 +10,11 @@ import { ActionContext, Box, Button, ButtonArea, List, VerticalSpace } from '../ import useTranslation from '../hooks/useTranslation'; import { Header } from '../partials'; -export default function Welcome (): React.ReactElement { +interface Props extends ThemeProps { + className?: string; +} + +const Welcome = function ({ className }: Props): React.ReactElement { const { t } = useTranslation(); const onAction = useContext(ActionContext); @@ -22,31 +26,29 @@ export default function Welcome (): React.ReactElement { return ( <>
('Welcome')} /> - {t('Before we start, just a couple of notes regarding use:')} - - -
  • {t('We do not send any clicks, pageviews or events to a central server')}
  • -
  • {t('We do not use any trackers or analytics')}
  • -
  • {t("We don't collect keys, addresses or any information - your information never leaves this machine")}
  • -
    -
    - {t('... we are not in the information collection business (even anonymized).')} +
    +

    {t('Before we start, just a couple of notes regarding use:')}

    + + +
  • {t('We do not send any clicks, pageviews or events to a central server')}
  • +
  • {t('We do not use any trackers or analytics')}
  • +
  • {t("We don't collect keys, addresses or any information - your information never leaves this machine")}
  • +
    +
    +

    {t('... we are not in the information collection business (even anonymized).')}

    +
    ); -} +}; -const Note = styled.p(({ theme }: Props) => ` - color: ${theme.subTextColor}; - margin-bottom: 6px; - margin-top: 0; -`); - -const TextBox = styled(Box)(({ theme }: Props) => ` - border: 1px solid ${theme.inputBorderColor}; - color: ${theme.subTextColor}; - margin: 0.75rem 24px; +export default styled(Welcome)(({ theme }: Props) => ` + p { + color: ${theme.subTextColor}; + margin-bottom: 6px; + margin-top: 0; + } `); diff --git a/packages/extension-ui/src/components/Address.tsx b/packages/extension-ui/src/components/Address.tsx index 0c2980d0..373672a7 100644 --- a/packages/extension-ui/src/components/Address.tsx +++ b/packages/extension-ui/src/components/Address.tsx @@ -186,6 +186,7 @@ function Address ({ actions, address, children, className, genesisHash, isExtern
    export default styled(Box)(({ theme }: ThemeProps) => ` background: ${theme.readonlyInputBackground}; - border: none; + border: 1px solid ${theme.inputBorderColor}; border-radius: ${theme.borderRadius}; - color: ${theme.textColor}; + color: ${theme.subTextColor}; font-family: ${theme.fontFamily}; font-size: ${theme.fontSize}; - margin: ${theme.boxMargin}; + margin: 0.75rem 24px; padding: ${theme.boxPadding}; position: relative; diff --git a/packages/extension-ui/src/components/ButtonArea.tsx b/packages/extension-ui/src/components/ButtonArea.tsx index ef39931d..97f4ec26 100644 --- a/packages/extension-ui/src/components/ButtonArea.tsx +++ b/packages/extension-ui/src/components/ButtonArea.tsx @@ -3,22 +3,32 @@ import { ThemeProps } from '../types'; +import React from 'react'; import styled from 'styled-components'; -import Button from './Button'; -export default styled.div(({ theme }: ThemeProps) => ` +interface Props extends ThemeProps { + className?: string; + children: React.ReactNode; +} + +const ButtonArea = function ({ children, className }: Props) { + return ( +
    + {children} +
    + ); +}; + +export default styled(ButtonArea)(({ theme }: ThemeProps) => ` display: flex; flex-direction: row; background: ${theme.highlightedAreaBackground}; border-top: 1px solid ${theme.inputBorderColor}; padding: 12px 24px; + margin-left: 0; + margin-right: 0; - &&& { - margin-left: 0; - margin-right: 0; - } - - & > ${Button}:not(:last-of-type) { + & > button:not(:last-of-type) { margin-right: 8px; } `); diff --git a/packages/extension-ui/src/components/List.tsx b/packages/extension-ui/src/components/List.tsx index c0c732b2..bdc5efc9 100644 --- a/packages/extension-ui/src/components/List.tsx +++ b/packages/extension-ui/src/components/List.tsx @@ -1,11 +1,24 @@ // Copyright 2019-2020 @polkadot/extension-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + import { ThemeProps } from '../types'; import styled from 'styled-components'; -export default styled.ul(({ theme }: ThemeProps) => ` +interface Props extends ThemeProps { + className?: string; + children: React.ReactNode; +} + +const List = ({ children, className }: Props) => ( +
      + {children} +
    +); + +export default styled(List)(({ theme }: ThemeProps) => ` list-style: none; padding-inline-start: 10px; padding-inline-end: 10px; diff --git a/packages/extension-ui/src/components/Main.tsx b/packages/extension-ui/src/components/Main.tsx new file mode 100644 index 00000000..00423878 --- /dev/null +++ b/packages/extension-ui/src/components/Main.tsx @@ -0,0 +1,40 @@ +// Copyright 2019-2020 @polkadot/extension-ui authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { ThemeProps } from '../types'; + +import React from 'react'; +import styled from 'styled-components'; + +interface Props { + children: React.ReactNode; + className?: string; +} + +function Main ({ children, className }: Props): React.ReactElement { + return ( +
    + {children} +
    + ); +} + +export default styled(Main)(({ theme }: ThemeProps) => ` + display: flex; + flex-direction: column; + height: 100%; + background: ${theme.background}; + color: ${theme.textColor}; + font-size: ${theme.fontSize}; + line-height: ${theme.lineHeight}; + border: 1px solid ${theme.inputBorderColor}; + + * { + font-family: ${theme.fontFamily}; + } + + > * { + padding-left: 24px; + padding-right: 24px; + } +`); diff --git a/packages/extension-ui/src/components/MenuItem.tsx b/packages/extension-ui/src/components/MenuItem.tsx index 7a3ac710..a3c43f7c 100644 --- a/packages/extension-ui/src/components/MenuItem.tsx +++ b/packages/extension-ui/src/components/MenuItem.tsx @@ -4,9 +4,9 @@ import React from 'react'; import styled from 'styled-components'; -import Title from './Title'; +import { ThemeProps } from '../types'; -interface Props { +interface Props extends ThemeProps { children: React.ReactNode; className?: string; noBorder?: boolean; @@ -17,23 +17,27 @@ function MenuItem ({ children, className = '', title }: Props): React.ReactEleme return (
    {title && ( - {title} +
    {title}
    )} {children}
    ); } -export default styled(MenuItem)` +export default styled(MenuItem)(({ theme }: ThemeProps) => ` min-width: 13rem; padding: 0 16px; max-width: 100%; > .itemTitle { - margin: 0; - } - - &+& { - margin-top: 16px; - } -`; + margin: 0; + width: 100%; + font-weight: 800; + font-size: 10px; + line-height: 14px; + letter-spacing: 0.04em; + text-transform: uppercase; + color: ${theme.textColor}; + opacity: 0.65; + } +`); diff --git a/packages/extension-ui/src/components/Svg.tsx b/packages/extension-ui/src/components/Svg.tsx index 27f0b957..1749d3dc 100644 --- a/packages/extension-ui/src/components/Svg.tsx +++ b/packages/extension-ui/src/components/Svg.tsx @@ -1,15 +1,20 @@ // Copyright 2019-2020 @polkadot/extension-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 +import React from 'react'; + import { ThemeProps } from '../types'; import styled from 'styled-components'; -interface Props { +interface Props extends ThemeProps { + className?: string; src: string; } -export default styled.span(({ src, theme }: Props & ThemeProps) => ` +const Svg = ({ className }: Props) => ; + +export default styled(Svg)(({ src, theme }: Props) => ` background: ${theme.textColor}; display: inline-block; mask: url(${src}); diff --git a/packages/extension-ui/src/components/Title.ts b/packages/extension-ui/src/components/Title.ts deleted file mode 100644 index ed5b9f5e..00000000 --- a/packages/extension-ui/src/components/Title.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2019-2020 @polkadot/extension-ui authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -import { ThemeProps } from '../types'; - -import styled from 'styled-components'; - -export default styled.div(({ theme }: ThemeProps) => ` - width: 100%; - margin-bottom: 8px; - margin-top: 18px; - font-weight: 800; - font-size: 10px; - line-height: 14px; - letter-spacing: 0.04em; - text-transform: uppercase; - color: ${theme.textColor}; - opacity: 0.65; -`); diff --git a/packages/extension-ui/src/components/ValidatedInput.tsx b/packages/extension-ui/src/components/ValidatedInput.tsx index c0374edc..6a0b5f7a 100644 --- a/packages/extension-ui/src/components/ValidatedInput.tsx +++ b/packages/extension-ui/src/components/ValidatedInput.tsx @@ -50,19 +50,17 @@ function ValidatedInput> ({ className, compone onChange={setValue} value={value} /> - {Result.isError(validationResult) && {validationResult.error.errorDescription}} + {Result.isError(validationResult) && {validationResult.error.errorDescription}}
    ); } -const ErrorMessage = styled.span` - display: block; - margin-top: -10px; - font-size: ${({ theme }: ThemeProps): string => theme.labelFontSize}; - line-height: ${({ theme }: ThemeProps): string => theme.labelLineHeight}; - color: ${({ theme }: ThemeProps): string => theme.errorColor}; -`; - -ErrorMessage.displayName = 'ErrorMessage'; - -export default ValidatedInput; +export default styled(ValidatedInput)(({ theme }: ThemeProps) => ` + .error { + display: block; + margin-top: -10px; + font-size: ${theme.labelFontSize}; + line-height: ${theme.labelLineHeight}; + color: ${theme.errorColor}; + } +`); diff --git a/packages/extension-ui/src/components/VerticalSpace.ts b/packages/extension-ui/src/components/VerticalSpace.ts deleted file mode 100644 index df1a1837..00000000 --- a/packages/extension-ui/src/components/VerticalSpace.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2019-2020 @polkadot/extension-ui authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -import styled from 'styled-components'; - -export default styled.div` - height: 100%; -`; diff --git a/packages/extension-ui/src/components/VerticalSpace.tsx b/packages/extension-ui/src/components/VerticalSpace.tsx new file mode 100644 index 00000000..a0edae0c --- /dev/null +++ b/packages/extension-ui/src/components/VerticalSpace.tsx @@ -0,0 +1,18 @@ +// Copyright 2019-2020 @polkadot/extension-ui authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; + +import styled from 'styled-components'; + +interface Props { + className?: string; +} + +function VerticalSpace ({ className }: Props): React.ReactElement { + return
    ; +} + +export default styled(VerticalSpace)` + height: 100%; +`; diff --git a/packages/extension-ui/src/components/View.tsx b/packages/extension-ui/src/components/View.tsx index 15807200..fae0c0fa 100644 --- a/packages/extension-ui/src/components/View.tsx +++ b/packages/extension-ui/src/components/View.tsx @@ -4,10 +4,10 @@ import { ThemeProps } from '../types'; import React, { useState } from 'react'; -import styled, { ThemeProvider, createGlobalStyle } from 'styled-components'; +import { ThemeProvider, createGlobalStyle } from 'styled-components'; // FIXME We should not import from index when this one is imported there as well -import { AvailableThemes, chooseTheme, themes, ThemeSwitchContext } from '.'; +import { AvailableThemes, chooseTheme, Main, themes, ThemeSwitchContext } from '.'; interface Props { children: React.ReactNode; @@ -49,24 +49,4 @@ const BodyTheme = createGlobalStyle` } `; -const Main = styled.main` - display: flex; - flex-direction: column; - height: 100%; - background: ${({ theme }: ThemeProps): string => theme.background}; - color: ${({ theme }: ThemeProps): string => theme.textColor}; - font-size: ${({ theme }: ThemeProps): string => theme.fontSize}; - line-height: ${({ theme }: ThemeProps): string => theme.lineHeight}; - border: 1px solid ${({ theme }: ThemeProps): string => theme.inputBorderColor}; - - * { - font-family: ${({ theme }: ThemeProps): string => theme.fontFamily}; - } - - > * { - padding-left: 24px; - padding-right: 24px; - } -`; - export default View; diff --git a/packages/extension-ui/src/components/index.ts b/packages/extension-ui/src/components/index.ts index c95db831..16a0b73d 100644 --- a/packages/extension-ui/src/components/index.ts +++ b/packages/extension-ui/src/components/index.ts @@ -21,6 +21,7 @@ export { default as Label } from './Label'; export { default as Link } from './Link'; export { default as List } from './List'; export { default as Loading } from './Loading'; +export { default as Main } from './Main'; export { default as Menu } from './Menu'; export { default as MenuDivider } from './MenuDivider'; export { default as MenuItem } from './MenuItem'; @@ -31,7 +32,6 @@ export { default as Svg } from './Svg'; export { default as Switch } from './Switch'; export { default as Table } from './Table'; export { default as TextAreaWithLabel } from './TextAreaWithLabel'; -export { default as Title } from './Title'; export { default as ValidatedInput } from './ValidatedInput'; export { default as VerticalSpace } from './VerticalSpace'; export { default as View } from './View'; diff --git a/packages/extension-ui/src/partials/Header.tsx b/packages/extension-ui/src/partials/Header.tsx index c898586f..5eb73c58 100644 --- a/packages/extension-ui/src/partials/Header.tsx +++ b/packages/extension-ui/src/partials/Header.tsx @@ -20,10 +20,11 @@ interface Props extends ThemeProps { showAdd?: boolean; showBackArrow?: boolean; showSettings?: boolean; + smallMargin?: boolean; text?: React.ReactNode; } -function Header ({ children, className, showAdd, showBackArrow, showSettings, text }: Props): React.ReactElement { +function Header ({ children, className = '', showAdd, showBackArrow, showSettings, smallMargin = false, text }: Props): React.ReactElement { const [isAddOpen, setShowAdd] = useState(false); const [isSettingsOpen, setShowSettings] = useState(false); const addRef = useRef(null); @@ -48,7 +49,7 @@ function Header ({ children, className, showAdd, showBackArrow, showSettings, te ); return ( -
    +
    {showBackArrow @@ -197,6 +198,9 @@ export default React.memo(styled(Header)(({ theme }: Props) => ` &:visited { color: ${theme.labelColor}; } + } + &.smallMargin { + margin-bottom: 15px; } `));