Browse Source

Feedback, add errors and warning on BreadCrumb

master
Loëck Vézien 7 years ago
parent
commit
371084ff3c
No known key found for this signature in database GPG Key ID: CBCDCE384E853AC4
  1. 35
      src/components/Breadcrumb/Step.js
  2. 56
      src/components/Breadcrumb/index.js
  3. 8
      src/components/Breadcrumb/stories.js
  4. 20
      src/components/CurrentAddress/index.js
  5. 2
      src/components/CurrentAddress/stories.js
  6. 172
      src/components/DeviceConfirm/index.js
  7. 4
      src/components/layout/Print.js
  8. 2
      src/components/modals/Receive/03-step-confirm-address.js
  9. 2
      src/components/modals/Receive/04-step-receive-funds.js
  10. 18
      src/components/modals/Receive/index.js
  11. 2
      src/styles/theme.js

35
src/components/Breadcrumb/Step.js

@ -5,11 +5,15 @@ import styled from 'styled-components'
import Box from 'components/base/Box' import Box from 'components/base/Box'
const RADIUS = 17 import IconCheck from 'icons/Check'
import IconCross from 'icons/Cross'
const RADIUS = 18
const Wrapper = styled(Box).attrs({ const Wrapper = styled(Box).attrs({
alignItems: 'center', alignItems: 'center',
color: p => (p.isActive ? 'wallet' : 'grey'), color: p =>
['active', 'valid'].includes(p.status) ? 'wallet' : p.status === 'error' ? 'alertRed' : 'grey',
grow: true, grow: true,
justifyContent: 'center', justifyContent: 'center',
})` })`
@ -19,18 +23,19 @@ const Wrapper = styled(Box).attrs({
font-size: 9px; font-size: 9px;
` `
const Number = styled(Box).attrs({ const StepNumber = styled(Box).attrs({
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
color: 'white', color: 'white',
bg: p => (p.isActive ? 'wallet' : 'fog'), bg: p =>
['active', 'valid'].includes(p.status) ? 'wallet' : p.status === 'error' ? 'alertRed' : 'fog',
ff: 'Rubik|Regular', ff: 'Rubik|Regular',
})` })`
border-radius: 50%; border-radius: 50%;
font-size: 10px; font-size: 10px;
height: ${RADIUS}px; height: ${RADIUS}px;
line-height: 10px; line-height: 10px;
transition: all ease-in-out 0.1s ${p => (p.isActive ? 0.4 : 0)}s; transition: all ease-in-out 0.1s ${p => (['active', 'valid'].includes(p.status) ? 0.4 : 0)}s;
width: ${RADIUS}px; width: ${RADIUS}px;
` `
@ -40,21 +45,29 @@ const Label = styled(Box).attrs({
})` })`
position: absolute; position: absolute;
margin-top: 23px; margin-top: 23px;
transition: color ease-in-out 0.1s ${p => (p.isActive ? 0.4 : 0)}s; transition: color ease-in-out 0.1s ${p => (['active', 'valid'].includes(p.status) ? 0.4 : 0)}s;
` `
type Props = { type Props = {
number: number, number: number,
isActive: boolean, status: 'next' | 'active' | 'valid' | 'error' | 'disable',
children: any, children: any,
} }
function Step(props: Props) { function Step(props: Props) {
const { number, isActive, children } = props const { number, status, children } = props
return ( return (
<Wrapper isActive={isActive}> <Wrapper status={status}>
<Number isActive={isActive}>{number}</Number> <StepNumber status={status}>
<Label isActive={isActive}>{children}</Label> {status === 'active' || status === 'next' ? (
number
) : status === 'valid' ? (
<IconCheck size={10} />
) : (
<IconCross size={10} />
)}
</StepNumber>
<Label status={status}>{children}</Label>
</Wrapper> </Wrapper>
) )
} }

56
src/components/Breadcrumb/index.js

@ -8,11 +8,6 @@ import styled from 'styled-components'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Step from './Step' import Step from './Step'
type Props = {
items: Array<Object>,
currentStep: number | string,
}
const Wrapper = styled(Box).attrs({ const Wrapper = styled(Box).attrs({
horizontal: true, horizontal: true,
alignItems: 'center', alignItems: 'center',
@ -46,24 +41,53 @@ const Bar = styled.div`
} }
` `
type Props = {
currentStep: number | string,
items: Array<Object>,
stepsDisabled: Array<number>,
stepsErrors: Array<number>,
}
class Breadcrumb extends PureComponent<Props> { class Breadcrumb extends PureComponent<Props> {
static defaultProps = {
stepsDisabled: [],
stepsErrors: [],
}
render() { render() {
const { items, currentStep, ...props } = this.props const { items, stepsDisabled, stepsErrors, currentStep, ...props } = this.props
const itemsLength = items.length const itemsLength = items.length
const start = 100 / itemsLength / 2 const start = 100 / itemsLength / 2
return ( return (
<Box {...props} relative> <Box {...props} relative>
<Wrapper> <Wrapper>
{items.map((item, i) => ( {items.map((item, i) => {
<Step let status = 'next'
key={i}
isActive={i <= parseInt(currentStep, 10)} const stepIndex = parseInt(currentStep, 10)
isFirst={i === 0}
number={i + 1} if (i === stepIndex) {
> status = 'active'
{item.label} }
</Step>
))} if (i < stepIndex) {
status = 'valid'
}
if (stepsErrors.includes(i)) {
status = 'error'
}
if (stepsDisabled.includes(i)) {
status = 'disable'
}
return (
<Step key={i} status={status} number={i + 1}>
{item.label}
</Step>
)
})}
</Wrapper> </Wrapper>
<Bar <Bar
start={start} start={start}

8
src/components/Breadcrumb/stories.js

@ -2,7 +2,7 @@
import React from 'react' import React from 'react'
import { storiesOf } from '@storybook/react' import { storiesOf } from '@storybook/react'
import { number } from '@storybook/addon-knobs' import { array, number } from '@storybook/addon-knobs'
import Breadcrumb from 'components/Breadcrumb' import Breadcrumb from 'components/Breadcrumb'
@ -16,9 +16,11 @@ stories.add('Breadcrumb', () => (
> >
<Breadcrumb <Breadcrumb
currentStep={number('currentStep', 1, { currentStep={number('currentStep', 1, {
min: 1, min: 0,
max: 4, max: 3,
})} })}
stepsDisabled={array('stepsDisabled', []).map(a => Number(a))}
stepsErrors={array('stepsErrors', []).map(a => Number(a))}
items={[ items={[
{ label: 'Amount' }, { label: 'Amount' },
{ label: 'Summary' }, { label: 'Summary' },

20
src/components/CurrentAddress/index.js

@ -6,7 +6,6 @@ import styled from 'styled-components'
import noop from 'lodash/noop' import noop from 'lodash/noop'
import type { Account } from '@ledgerhq/wallet-common/lib/types'
import type { T } from 'types/common' import type { T } from 'types/common'
import { rgba } from 'styles/helpers' import { rgba } from 'styles/helpers'
@ -101,9 +100,9 @@ const FooterButton = ({
) )
type Props = { type Props = {
account: Account, address: string,
addressVerified: null | boolean, addressVerified?: boolean,
amount: null | string, amount?: string,
onCopy: Function, onCopy: Function,
onPrint: Function, onPrint: Function,
onShare: Function, onShare: Function,
@ -131,7 +130,7 @@ class CurrentAddress extends PureComponent<Props> {
render() { render() {
const { const {
account, address,
addressVerified, addressVerified,
amount, amount,
onCopy, onCopy,
@ -153,10 +152,7 @@ class CurrentAddress extends PureComponent<Props> {
<WrapperAddress notValid={notValid} grow> <WrapperAddress notValid={notValid} grow>
{withQRCode && ( {withQRCode && (
<Box mb={4}> <Box mb={4}>
<QRCode <QRCode size={150} data={`bitcoin:${address}${amount ? `?amount=${amount}` : ''}`} />
size={150}
data={`bitcoin:${account.address}${amount ? `?amount=${amount}` : ''}`}
/>
</Box> </Box>
)} )}
<Label> <Label>
@ -164,7 +160,7 @@ class CurrentAddress extends PureComponent<Props> {
<IconInfoCircle size={12} /> <IconInfoCircle size={12} />
</Label> </Label>
<Address withQRCode={withQRCode} notValid={notValid}> <Address withQRCode={withQRCode} notValid={notValid}>
{account.address} {address}
</Address> </Address>
</WrapperAddress> </WrapperAddress>
{withBadge && ( {withBadge && (
@ -184,13 +180,13 @@ class CurrentAddress extends PureComponent<Props> {
<FooterButton icon={<IconCheck size={16} />} label="Verify" onClick={onVerify} /> <FooterButton icon={<IconCheck size={16} />} label="Verify" onClick={onVerify} />
)} )}
<CopyToClipboard <CopyToClipboard
data={account.address} data={address}
render={copy => ( render={copy => (
<FooterButton icon={<IconCopy size={16} />} label="Copy" onClick={copy} /> <FooterButton icon={<IconCopy size={16} />} label="Copy" onClick={copy} />
)} )}
/> />
<Print <Print
data={{ account, amount }} data={{ address, amount }}
render={(print, isLoading) => ( render={(print, isLoading) => (
<FooterButton <FooterButton
icon={<IconPrint size={16} />} icon={<IconPrint size={16} />}

2
src/components/CurrentAddress/stories.js

@ -12,7 +12,7 @@ const stories = storiesOf('Components', module)
stories.add('CurrentAddress', () => ( stories.add('CurrentAddress', () => (
<CurrentAddress <CurrentAddress
account={accounts[0]} address={accounts[0].address}
addressVerified={boolean('addressVerified', true)} addressVerified={boolean('addressVerified', true)}
withBadge={boolean('withBadge', false)} withBadge={boolean('withBadge', false)}
withFooter={boolean('withFooter', false)} withFooter={boolean('withFooter', false)}

172
src/components/DeviceConfirm/index.js

@ -27,23 +27,23 @@ const Wrapper = styled(Box).attrs({
relative: true, relative: true,
})` })`
padding-top: ${p => (p.notValid ? 0 : 30)}px; padding-top: ${p => (p.notValid ? 0 : 30)}px;
transition: all ease-in-out 0.1s; transition: color ease-in-out 0.1s;
` `
const WrapperIcon = styled(Box)` const WrapperIcon = styled(Box)`
color: ${p => (p.notValid ? p.theme.colors.alertRed : p.theme.colors.positiveGreen)}; color: ${p => (p.notValid ? p.theme.colors.alertRed : p.theme.colors.positiveGreen)};
position: absolute; position: absolute;
left: 193px; left: ${p => (p.notValid ? 152 : 193)}px;
bottom: 16px; bottom: 16px;
svg { svg {
transition: all ease-in-out 0.1s; transition: color ease-in-out 0.1s;
} }
` `
const Check = ({ notValid }: { notValid: boolean }) => ( const Check = ({ notValid }: { notValid: boolean }) => (
<WrapperIcon notValid={notValid}> <WrapperIcon notValid={notValid}>
{notValid ? <IconCross size={11} /> : <IconCheck size={11} />} {notValid ? <IconCross size={10} /> : <IconCheck size={10} />}
</WrapperIcon> </WrapperIcon>
) )
@ -77,99 +77,97 @@ type Props = {
notValid: boolean, notValid: boolean,
} }
const DeviceConfirm = (props: Props) => ( const SVG = (
<Wrapper {...props}> <svg width="365" height="44">
{!props.notValid && <PushButton />} <defs>
<Check notValid={props.notValid} /> <rect id="DeviceConfirm-a" width="41.7112299" height="238.383838" rx="4.00000006" />
<svg width="365" height="44"> <rect
<defs> id="DeviceConfirm-b"
<rect id="a" width="41.7112299" height="238.383838" rx="4.00000006" /> width="21.1764706"
height="62.0185596"
x="10.2673797"
y="20.6728532"
rx="1.60000002"
/>
<path
id="DeviceConfirm-c"
d="M20.855615 94.9659194c11.5182381 0 20.8556149 9.3373766 20.8556149 20.8556146v118.562304c0 2.209139-1.790861 4-4 4H4.00000006c-2.20913903 0-4.00000006-1.790861-4.00000006-4V115.821534c0-11.518238 9.33737688-20.8556146 20.855615-20.8556146z"
/>
<linearGradient id="DeviceConfirm-d" x1="50%" x2="50%" y1="0%" y2="100%">
<stop offset="0%" />
<stop offset="100%" stopColor="#FFF" />
</linearGradient>
</defs>
<g fill="none" fillRule="evenodd">
<g transform="rotate(-90 85.0909095 -41.5252525)">
<rect <rect
id="b" width="4.49197861"
width="21.1764706" height="17.1197066"
height="62.0185596" x="38.3363042"
x="10.2673797" y="15.5046399"
y="20.6728532" fill="#142533"
rx="1.60000002" rx="2"
/> />
<path <rect
id="c" width="4.49197861"
d="M20.855615 94.9659194c11.5182381 0 20.8556149 9.3373766 20.8556149 20.8556146v118.562304c0 2.209139-1.790861 4-4 4H4.00000006c-2.20913903 0-4.00000006-1.790861-4.00000006-4V115.821534c0-11.518238 9.33737688-20.8556146 20.855615-20.8556146z" height="17.1197066"
x="38.3363042"
y="70.0938929"
fill="#142533"
rx="2"
/> />
<linearGradient id="d" x1="50%" x2="50%" y1="0%" y2="100%"> <use fill="#FFF" xlinkHref="#DeviceConfirm-a" />
<stop offset="0%" /> <use fill="currentColor" fillOpacity=".14999999" xlinkHref="#DeviceConfirm-a" />
<stop offset="100%" stopColor="#FFF" /> <rect
</linearGradient> width="39.7112299"
</defs> height="236.383838"
<g fill="none" fillRule="evenodd"> x="1"
<g transform="rotate(-90 85.0909095 -41.5252525)"> y="1"
<rect stroke="#142533"
width="4.49197861"
height="17.1197066"
x="38.3363042"
y="15.5046399"
fill="#142533"
rx="2"
/>
<rect
width="4.49197861"
height="17.1197066"
x="38.3363042"
y="70.0938929"
fill="#142533"
rx="2"
/>
<use fill="#FFF" xlinkHref="#a" />
<use fill="currentColor" fillOpacity=".14999999" xlinkHref="#a" />
<rect
width="39.7112299"
height="236.383838"
x="1"
y="1"
stroke="#142533"
strokeWidth="2"
rx="4.00000006"
/>
<use fill="#FFF" xlinkHref="#b" />
<rect
width="20.1764706"
height="61.0185596"
x="10.7673797"
y="21.1728532"
stroke="currentColor"
rx="1.60000002"
/>
<use fill="#FFF" xlinkHref="#c" />
<path
stroke="#142533"
strokeWidth="2"
d="M20.855615 95.9659194C9.88966163 95.9659194 1 104.855581 1 115.821534v118.562304c0 1.656855 1.34314578 3 3.00000006 3H37.7112299c1.6568543 0 3-1.343145 3-3V115.821534c0-10.965953-8.8896616-19.8556146-19.8556149-19.8556146z"
/>
<ellipse
cx="21.0160428"
cy="116.123293"
stroke="#142533"
rx="10.5695187"
ry="10.6439599"
/>
</g>
<path
stroke="#1D2027"
strokeWidth="2" strokeWidth="2"
d="M126.9617746 31.060606c0 .55228475-.4477153 1-1 1H99.7373741c-2.7614237 0-5-2.23857625-5-5v-8.4856683c0-2.7614238 2.2385763-5 5-5h26.2244005c.5522847 0 1 .4477152 1 1V31.060606z" rx="4.00000006"
/> />
<use fill="#FFF" xlinkHref="#DeviceConfirm-b" />
<rect
width="20.1764706"
height="61.0185596"
x="10.7673797"
y="21.1728532"
stroke="currentColor"
rx="1.60000002"
/>
<use fill="#FFF" xlinkHref="#DeviceConfirm-c" />
<path <path
stroke="#142533" stroke="#142533"
strokeWidth="2" strokeWidth="2"
d="M94.3535357 25.85229841H83.4242428V19.4170232h10.9292929v6.43527521z" d="M20.855615 95.9659194C9.88966163 95.9659194 1 104.855581 1 115.821534v118.562304c0 1.656855 1.34314578 3 3.00000006 3H37.7112299c1.6568543 0 3-1.343145 3-3V115.821534c0-10.965953-8.8896616-19.8556146-19.8556149-19.8556146z"
/>
<path
fill="url(#d)"
d="M6.83618598 57.9245106h1.61616161v82.6510534H6.83618598V57.9245106zm5.65656562 0h1.6161617v82.6510534h-1.6161617V57.9245106z"
transform="matrix(0 -1 -1 0 140.606061 33.060606)"
/> />
<ellipse cx="21.0160428" cy="116.123293" stroke="#142533" rx="10.5695187" ry="10.6439599" />
</g> </g>
</svg> <path
stroke="#1D2027"
strokeWidth="2"
d="M126.9617746 31.060606c0 .55228475-.4477153 1-1 1H99.7373741c-2.7614237 0-5-2.23857625-5-5v-8.4856683c0-2.7614238 2.2385763-5 5-5h26.2244005c.5522847 0 1 .4477152 1 1V31.060606z"
/>
<path
stroke="#142533"
strokeWidth="2"
d="M94.3535357 25.85229841H83.4242428V19.4170232h10.9292929v6.43527521z"
/>
<path
fill="url(#DeviceConfirm-d)"
d="M6.83618598 57.9245106h1.61616161v82.6510534H6.83618598V57.9245106zm5.65656562 0h1.6161617v82.6510534h-1.6161617V57.9245106z"
transform="matrix(0 -1 -1 0 140.606061 33.060606)"
/>
</g>
</svg>
)
const DeviceConfirm = (props: Props) => (
<Wrapper {...props}>
{!props.notValid && <PushButton />}
<Check notValid={props.notValid} />
{SVG}
</Wrapper> </Wrapper>
) )

4
src/components/layout/Print.js

@ -31,12 +31,12 @@ class Print extends PureComponent<any> {
if (!data) { if (!data) {
return null return null
} }
const { account, amount } = data const { address, amount } = data
return ( return (
<CurrentAddress <CurrentAddress
innerRef={n => (this._node = n)} innerRef={n => (this._node = n)}
amount={amount} amount={amount}
account={account} address={address}
withQRCode withQRCode
/> />
) )

2
src/components/modals/Receive/03-step-confirm-address.js

@ -42,7 +42,7 @@ export default (props: Props) => (
<Title>{props.t('receive:steps.confirmAddress.action')}</Title> <Title>{props.t('receive:steps.confirmAddress.action')}</Title>
<Text>{props.t('receive:steps.confirmAddress.text')}</Text> <Text>{props.t('receive:steps.confirmAddress.text')}</Text>
{props.account && ( {props.account && (
<CurrentAddress addressVerified={props.addressVerified} account={props.account} /> <CurrentAddress addressVerified={props.addressVerified} address={props.account.address} />
)} )}
{props.device && {props.device &&
props.account && ( props.account && (

2
src/components/modals/Receive/04-step-receive-funds.js

@ -36,7 +36,7 @@ export default (props: Props) => (
/> />
</Box> </Box>
<CurrentAddress <CurrentAddress
account={props.account} address={props.account && props.account.address}
addressVerified={props.addressVerified} addressVerified={props.addressVerified}
amount={props.amount} amount={props.amount}
onVerify={props.onVerify} onVerify={props.onVerify}

18
src/components/modals/Receive/index.js

@ -35,11 +35,13 @@ type Props = {
type State = { type State = {
account: Account | null, account: Account | null,
amount: string | number,
addressVerified: null | boolean, addressVerified: null | boolean,
amount: string | number,
appStatus: null | string, appStatus: null | string,
deviceSelected: Device | null, deviceSelected: Device | null,
stepIndex: number, stepIndex: number,
stepsDisabled: Array<number>,
stepsErrors: Array<number>,
} }
const GET_STEPS = t => [ const GET_STEPS = t => [
@ -56,6 +58,8 @@ const INITIAL_STATE = {
appStatus: null, appStatus: null,
deviceSelected: null, deviceSelected: null,
stepIndex: 0, stepIndex: 0,
stepsDisabled: [],
stepsErrors: [],
} }
class ReceiveModal extends PureComponent<Props, State> { class ReceiveModal extends PureComponent<Props, State> {
@ -137,6 +141,7 @@ class ReceiveModal extends PureComponent<Props, State> {
deviceSelected: null, deviceSelected: null,
appStatus: null, appStatus: null,
addressVerified: null, addressVerified: null,
stepsErrors: [],
stepIndex: newStepIndex, stepIndex: newStepIndex,
}) })
} }
@ -150,6 +155,7 @@ class ReceiveModal extends PureComponent<Props, State> {
handleCheckAddress = isVerified => { handleCheckAddress = isVerified => {
this.setState({ this.setState({
addressVerified: isVerified, addressVerified: isVerified,
stepsErrors: isVerified === false ? [2] : [],
}) })
if (isVerified === true) { if (isVerified === true) {
@ -172,6 +178,7 @@ class ReceiveModal extends PureComponent<Props, State> {
handleSkipStep = () => handleSkipStep = () =>
this.setState({ this.setState({
addressVerified: false, addressVerified: false,
stepsErrors: [],
stepIndex: this._steps.length - 1, // last step stepIndex: this._steps.length - 1, // last step
}) })
@ -252,7 +259,7 @@ class ReceiveModal extends PureComponent<Props, State> {
render() { render() {
const { t } = this.props const { t } = this.props
const { stepIndex } = this.state const { stepsErrors, stepIndex } = this.state
const canClose = this.canClose() const canClose = this.canClose()
const canPrev = this.canPrev() const canPrev = this.canPrev()
@ -277,7 +284,12 @@ class ReceiveModal extends PureComponent<Props, State> {
{t('receive:title')} {t('receive:title')}
</ModalTitle> </ModalTitle>
<ModalContent> <ModalContent>
<Breadcrumb mb={5} currentStep={stepIndex} items={this._steps} /> <Breadcrumb
mb={5}
currentStep={stepIndex}
stepsErrors={stepsErrors}
items={this._steps}
/>
{this.renderStep()} {this.renderStep()}
</ModalContent> </ModalContent>
{stepIndex !== 3 && {stepIndex !== 3 &&

2
src/styles/theme.js

@ -66,7 +66,7 @@ export const colors = {
pearl: '#ff0000', pearl: '#ff0000',
// new colors // new colors
alertRed: '#fa4352', alertRed: '#ea2e49',
black: '#000000', black: '#000000',
dark: '#142533', dark: '#142533',
fog: '#d8d8d8', fog: '#d8d8d8',

Loading…
Cancel
Save