Browse Source

feat(ui): enhancements and fixes for UI components

- Reset global link styles
- Support align (left, right, center) in Header component
- Support label, description in Input elements
- Add validation for `required` prop in Input elements
- Add asterisk in label for required form inputs
- Adjust icon positioning on form error messages
- Ability to include logo in Modal element
- Adjust size and positioning of Modal close button
- Add PasswordInput
- Add Span element
- Coerce to string in Truncate component
next
Tom Kirkpatrick 6 years ago
parent
commit
a0da5bc696
No known key found for this signature in database GPG Key ID: 72203A8EC5967EA8
  1. 8
      app/components/UI/DataRow.js
  2. 9
      app/components/UI/GlobalStyle.js
  3. 26
      app/components/UI/Header.js
  4. 2
      app/components/UI/Heading.js
  5. 105
      app/components/UI/Input.js
  6. 1
      app/components/UI/Label.js
  7. 15
      app/components/UI/MainContent.js
  8. 25
      app/components/UI/Message.js
  9. 68
      app/components/UI/Modal.js
  10. 2
      app/components/UI/Page.js
  11. 13
      app/components/UI/Panel.js
  12. 48
      app/components/UI/PasswordInput.js
  13. 15
      app/components/UI/Sidebar.js
  14. 13
      app/components/UI/Span.js
  15. 64
      app/components/UI/TextArea.js
  16. 12
      app/components/UI/Truncate.js
  17. 2
      app/components/UI/index.js
  18. 109
      stories/forms/form.stories.js
  19. 3
      test/unit/components/Pay/__snapshots__/PayHeader.spec.js.snap
  20. 5
      test/unit/components/Pay/__snapshots__/PaySummaryRow.spec.js.snap
  21. 75
      test/unit/components/UI/__snapshots__/Header.spec.js.snap
  22. 2
      test/unit/components/UI/__snapshots__/Heading.spec.js.snap
  23. 11
      test/unit/components/UI/__snapshots__/Input.spec.js.snap
  24. 10
      test/unit/components/UI/__snapshots__/LightningInvoiceInput.spec.js.snap
  25. 2
      test/unit/components/UI/__snapshots__/MainContent.spec.js.snap
  26. 26
      test/unit/components/UI/__snapshots__/Message.spec.js.snap
  27. 87
      test/unit/components/UI/__snapshots__/Modal.spec.js.snap
  28. 2
      test/unit/components/UI/__snapshots__/Page.spec.js.snap
  29. 4
      test/unit/components/UI/__snapshots__/Radio.spec.js.snap
  30. 11
      test/unit/components/UI/__snapshots__/Select.spec.js.snap
  31. 2
      test/unit/components/UI/__snapshots__/Sidebar.spec.js.snap
  32. 11
      test/unit/components/UI/__snapshots__/TextArea.spec.js.snap

8
app/components/UI/DataRow.js

@ -3,12 +3,12 @@ import PropTypes from 'prop-types'
import { Flex } from 'rebass' import { Flex } from 'rebass'
import { Text } from 'components/UI' import { Text } from 'components/UI'
const DataRow = ({ left, right }) => ( const DataRow = ({ left, right, ...rest }) => (
<Flex alignItems="center" py={3}> <Flex alignItems="center" py={3} {...rest} justifyContent="space-between">
<Text width={1 / 2} fontWeight="normal"> <Text width={2 / 5} fontWeight="normal">
{left} {left}
</Text> </Text>
<Text width={1 / 2} textAlign="right"> <Text width={3 / 5} textAlign="right">
{right} {right}
</Text> </Text>
</Flex> </Flex>

9
app/components/UI/GlobalStyle.js

@ -41,6 +41,15 @@ const GlobalStyle = createGlobalStyle`
font-size: 13px; font-size: 13px;
} }
a {
text-decoration: none;
color: inherit;
}
pre {
font-family: "Lucida Console", Monaco, monospace;
}
#root { #root {
height: 100%; height: 100%;
} }

26
app/components/UI/Header.js

@ -3,23 +3,29 @@ import PropTypes from 'prop-types'
import { Flex } from 'rebass' import { Flex } from 'rebass'
import { Heading, Text } from 'components/UI' import { Heading, Text } from 'components/UI'
const Header = ({ title, subtitle, logo }) => ( const Header = ({ title, subtitle, align, logo }) => (
<Flex alignItems="center" as="header" flexDirection="column"> <Flex alignItems={align} as="header" flexDirection="column" justifyContent={align}>
{logo && ( {logo && (
<Flex alignItems="center" justifyContent="center"> <Text textAlign={align} fontSize="50px" lineHeight="1em" css={{ height: '50px' }}>
<Text fontSize="50px" lineHeight="1em" css={{ height: '50px' }}> {logo}
{logo} </Text>
</Text>
</Flex>
)} )}
{title && <Heading.h1>{title}</Heading.h1>} {title && (
{subtitle && <Heading.h4>{subtitle}</Heading.h4>} <Heading.h1 textAlign={align} mb={1}>
{title}
</Heading.h1>
)}
{subtitle && <Heading.h4 textAlign={align}>{subtitle}</Heading.h4>}
</Flex> </Flex>
) )
Header.propTypes = { Header.propTypes = {
title: PropTypes.node, title: PropTypes.node,
subtitle: PropTypes.node, subtitle: PropTypes.node,
logo: PropTypes.node logo: PropTypes.node,
align: PropTypes.string
}
Header.defaultProps = {
align: 'center'
} }
export default Header export default Header

2
app/components/UI/Heading.js

@ -14,7 +14,7 @@ class Heading extends React.PureComponent {
return ( return (
<BaseHeading <BaseHeading
as="h2" as="h2"
lineHeight="1.4" lineHeight="1.2"
fontWeight="light" fontWeight="light"
fontSize={5} fontSize={5}
color="primaryText" color="primaryText"

105
app/components/UI/Input.js

@ -1,10 +1,13 @@
/* eslint-disable react/no-multi-comp */
import React from 'react' import React from 'react'
import { asField } from 'informed' import { asField } from 'informed'
import { withTheme } from 'styled-components' import { withTheme } from 'styled-components'
import system from '@rebass/components' import system from '@rebass/components'
import { styles } from 'styled-system' import { styles } from 'styled-system'
import * as yup from 'yup'
import { Flex } from 'rebass' import { Flex } from 'rebass'
import { Message } from 'components/UI' import { Message, Label, Span, Text } from 'components/UI'
// Create an html input element that accepts all style props from styled-system. // Create an html input element that accepts all style props from styled-system.
const SystemInput = system( const SystemInput = system(
@ -21,7 +24,15 @@ const SystemInput = system(
p: 3, p: 3,
width: 1 width: 1
}, },
...Object.keys(styles) 'space',
'color',
'borders',
'borderColor',
'borderRadius',
'fontFamily',
'fontSize',
'fontWeight',
'width'
) )
/** /**
@ -33,6 +44,12 @@ const SystemInput = system(
class Input extends React.Component { class Input extends React.Component {
static displayName = 'Input' static displayName = 'Input'
static defaultProps = {
description: null,
label: null,
showMessage: true
}
state = { state = {
hasFocus: false hasFocus: false
} }
@ -46,16 +63,33 @@ class Input extends React.Component {
render() { render() {
const { const {
css, css,
description,
onChange, onChange,
onBlur, onBlur,
onFocus, onFocus,
forwardedRef, forwardedRef,
label,
required,
theme, theme,
field,
fieldApi, fieldApi,
fieldState, fieldState,
justifyContent, justifyContent,
showMessage,
validate,
variant,
...rest ...rest
} = this.props } = this.props
// Extract any styled-system space props so that we can apply them directly to the wrapper.
const spaceProps = {}
Object.keys(rest).forEach(key => {
if ([...Object.keys(styles.space.propTypes), 'width'].includes(key)) {
spaceProps[key] = rest[key]
delete rest[key]
}
})
const { readOnly } = this.props const { readOnly } = this.props
const { hasFocus } = this.state const { hasFocus } = this.state
const { setValue, setTouched } = fieldApi const { setValue, setTouched } = fieldApi
@ -66,15 +100,28 @@ class Input extends React.Component {
let borderColor let borderColor
if (readOnly) { if (readOnly) {
borderColor = theme.colors.gray borderColor = theme.colors.gray
} else if (fieldState.error) { } else if (fieldState.error || fieldState.asyncError) {
borderColor = theme.colors.superRed borderColor = theme.colors.superRed
} else if (value && !fieldState.error) { } else if (value && (!fieldState.error && !fieldState.asyncError)) {
borderColor = theme.colors.superGreen borderColor = theme.colors.superGreen
} }
return ( return (
<Flex flexDirection="column" justifyContent={justifyContent}> <Flex flexDirection="column" justifyContent={justifyContent} {...spaceProps}>
{label && (
<Label htmlFor={field} mb={2}>
{label}
{required && (
<Span fontSize="s" css={{ 'vertical-align': 'super' }}>
{' '}
*
</Span>
)}
</Label>
)}
<SystemInput <SystemInput
p={variant === 'thin' ? 2 : 3}
width={1}
borderColor={borderColor || theme.colors.gray} borderColor={borderColor || theme.colors.gray}
css={Object.assign( css={Object.assign(
{ {
@ -88,6 +135,7 @@ class Input extends React.Component {
css css
)} )}
{...rest} {...rest}
field={field}
ref={this.inputRef} ref={this.inputRef}
value={!value && value !== 0 ? '' : value} value={!value && value !== 0 ? '' : value}
onChange={e => { onChange={e => {
@ -117,16 +165,51 @@ class Input extends React.Component {
onFocus(e) onFocus(e)
} }
}} }}
error={fieldState.error} required={required}
/> />
{fieldState.error && ( {description && (
<Message variant={hasFocus ? 'warning' : 'error'} justifyContent={justifyContent} mt={2}> <Text color="gray" fontSize="s" mt={1}>
{fieldState.error} {description}
</Message> </Text>
)} )}
{showMessage &&
(fieldState.error || fieldState.asyncError) && (
<Message variant={hasFocus ? 'warning' : 'error'} mt={1}>
{fieldState.error || fieldState.asyncError}
</Message>
)}
</Flex> </Flex>
) )
} }
} }
export default asField(withTheme(Input)) const InputAsField = asField(Input)
class WrappedInputAsField extends React.Component {
validate = value => {
const { disabled, required } = this.props
if (disabled) {
return
}
try {
if (required) {
const validator = yup.string().required()
validator.validateSync(value)
}
} catch (error) {
return error.message
}
// Run any additional validation provided by the caller.
const { validate } = this.props
if (validate) {
return validate(value)
}
}
render() {
return <InputAsField validate={this.validate} {...this.props} />
}
}
export default withTheme(WrappedInputAsField)

1
app/components/UI/Label.js

@ -10,7 +10,6 @@ const SystemLabel = system(
color: 'primaryText', color: 'primaryText',
fontSize: 'm', fontSize: 'm',
fontWeight: 'normal', fontWeight: 'normal',
width: 1,
mb: 1 mb: 1
}, },
...Object.keys(styles) ...Object.keys(styles)

15
app/components/UI/MainContent.js

@ -7,6 +7,19 @@ import { BackgroundPrimary } from 'components/UI'
* @example * @example
* <MainContent>Some content</MainContent> * <MainContent>Some content</MainContent>
*/ */
const MainContent = props => <BackgroundPrimary as="article" width={1} {...props} /> const MainContent = ({ css, ...rest }) => (
<BackgroundPrimary
as="article"
width={1}
{...rest}
css={Object.assign(
{
position: 'relative',
'overflow-y': 'scroll'
},
css
)}
/>
)
export default MainContent export default MainContent

25
app/components/UI/Message.js

@ -25,19 +25,26 @@ class Message extends React.Component {
children: PropTypes.node children: PropTypes.node
} }
renderIcon = () => {
const { variant } = this.props
return (
<Box mr={1} width="14px" css={{ height: '14px' }}>
{variant === 'success' && <Success height="14px" width="14px" />}
{variant === 'warning' && <Warning height="14px" width="14px" />}
{variant === 'error' && <Error height="14px" width="14px" />}
</Box>
)
}
render() { render() {
const { children, variant, ...rest } = this.props const { children, variant, ...rest } = this.props
return ( return (
<StyledMessage {...rest} variant={variant} alignItems="center"> <Text fontSize="s" fontWeight="normal" {...rest}>
<Box mr={1}> <StyledMessage variant={variant} alignItems="center">
{variant === 'success' && <Success />} {this.renderIcon()}
{variant === 'warning' && <Warning />}
{variant === 'error' && <Error />}
</Box>
<Text fontSize="s" fontWeight="normal">
{children} {children}
</Text> </StyledMessage>
</StyledMessage> </Text>
) )
} }
} }

68
app/components/UI/Modal.js

@ -2,6 +2,8 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Box, Flex } from 'rebass' import { Box, Flex } from 'rebass'
import X from 'components/Icon/X' import X from 'components/Icon/X'
import ZapLogo from 'components/Icon/ZapLogo'
import { Panel, Text } from 'components/UI'
/** /**
* @render react * @render react
@ -14,42 +16,50 @@ class Modal extends React.Component {
static propTypes = { static propTypes = {
children: PropTypes.node, children: PropTypes.node,
onClose: PropTypes.func onClose: PropTypes.func,
withClose: PropTypes.bool,
withHeader: PropTypes.bool
} }
state = { static defaultProps = {
hover: false withClose: true,
} withHeader: false
hoverOn = () => {
this.setState({ hover: true })
}
hoverOff = () => {
this.setState({ hover: false })
} }
render() { render() {
const { hover } = this.state const { children, onClose, withClose, withHeader, ...rest } = this.props
const { children, onClose } = this.props
return ( return (
<Flex flexDirection="column" width={1} p={3} bg="primaryColor" css={{ height: '100%' }}> <Panel width={1} bg="primaryColor" color="primaryText">
<Flex justifyContent="flex-end" as="header" color="primaryText"> <Panel.Header p={3}>
<Box <Flex justifyContent="space-between">
css={{ cursor: 'pointer', opacity: hover ? 0.6 : 1 }} <Box
ml="auto" css={{ height: '40px', cursor: 'pointer', opacity: 0.6, '&:hover': { opacity: 1 } }}
onClick={onClose} ml="auto"
onMouseEnter={this.hoverOn} onClick={onClose}
onMouseLeave={this.hoverOff} p={2}
p={2} >
> {withClose && <X width={20} height={20} />}
<X width="2em" height="2em" /> </Box>
</Box> </Flex>
</Flex> {withHeader && (
<Box as="section" p={3} pt={1} css={{ flex: 1 }}> <Flex justifyContent="space-between" px={3}>
<Box color="primaryText">
<ZapLogo width="70px" height="32px" />
</Box>
<Text
fontWeight="normal"
css={{ cursor: 'pointer', opacity: 0.6, '&:hover': { opacity: 1 } }}
>
Need Help?
</Text>
</Flex>
)}
</Panel.Header>
<Panel.Body px={4} pb={4} {...rest}>
{' '}
{children} {children}
</Box> </Panel.Body>
</Flex> </Panel>
) )
} }
} }

2
app/components/UI/Page.js

@ -14,9 +14,9 @@ const Page = ({ css, ...rest }) => (
css={Object.assign( css={Object.assign(
{ {
height: '100%', height: '100%',
overflow: 'hidden',
'min-width': '950px', 'min-width': '950px',
'min-height': '600px', 'min-height': '600px',
'overflow-y': 'hidden',
'box-shadow': '0 20px 70px rgba(0, 0, 0, 0.55)' 'box-shadow': '0 20px 70px rgba(0, 0, 0, 0.55)'
}, },
css css

13
app/components/UI/Panel.js

@ -9,8 +9,17 @@ const PanelHeader = ({ children, ...rest }) => (
) )
PanelHeader.propTypes = { children: PropTypes.node } PanelHeader.propTypes = { children: PropTypes.node }
const PanelBody = ({ children, ...rest }) => ( const PanelBody = ({ children, css, ...rest }) => (
<Box {...rest} as="section" css={{ flex: 1 }}> <Box
{...rest}
as="section"
css={Object.assign(
{
flex: 1
},
css
)}
>
{children} {children}
</Box> </Box>
) )

48
app/components/UI/PasswordInput.js

@ -0,0 +1,48 @@
/* eslint-disable react/no-multi-comp */
import React from 'react'
import { asField } from 'informed'
import * as yup from 'yup'
import Input from 'components/UI/Input'
/**
* @render react
* @name PasswordInput
*/
class PasswordInput extends React.Component {
render() {
return <Input {...this.props} type="password" />
}
}
const PasswordInputAsField = asField(PasswordInput)
class WrappedPasswordInputAsField extends React.Component {
validate = value => {
const { disabled, required } = this.props
if (disabled) {
return
}
try {
let validator = yup.string().min(8)
if (required) {
validator = validator.required()
}
validator.validateSync(value)
} catch (error) {
return error.message
}
// Run any additional validation provided by the caller.
const { validate } = this.props
if (validate) {
return validate(value)
}
}
render() {
return <PasswordInputAsField validate={this.validate} {...this.props} />
}
}
export default WrappedPasswordInputAsField

15
app/components/UI/Sidebar.js

@ -7,7 +7,20 @@ import { BackgroundTertiary } from 'components/UI'
* @example * @example
* <Sidebar>Some content</Sidebar> * <Sidebar>Some content</Sidebar>
*/ */
const Sidebar = props => <BackgroundTertiary as="aside" width={4 / 12} {...props} /> const Sidebar = ({ css, ...rest }) => (
<BackgroundTertiary
as="aside"
width={4 / 12}
{...rest}
css={Object.assign(
{
position: 'relative',
'overflow-y': 'scroll'
},
css
)}
/>
)
Sidebar.small = props => <Sidebar {...props} width={3 / 12} /> Sidebar.small = props => <Sidebar {...props} width={3 / 12} />
Sidebar.medium = props => <Sidebar {...props} width={4 / 12} /> Sidebar.medium = props => <Sidebar {...props} width={4 / 12} />

13
app/components/UI/Span.js

@ -0,0 +1,13 @@
import system from '@rebass/components'
const Span = system(
{
as: 'span'
},
'space',
'color',
'fontSize',
'fontWeight'
)
export default Span

64
app/components/UI/TextArea.js

@ -4,7 +4,7 @@ import system from '@rebass/components'
import { styles } from 'styled-system' import { styles } from 'styled-system'
import { withTheme } from 'styled-components' import { withTheme } from 'styled-components'
import { Flex } from 'rebass' import { Flex } from 'rebass'
import { Message } from 'components/UI' import { Message, Label, Span, Text } from 'components/UI'
// Create an html textarea element that accepts all style props from styled-system. // Create an html textarea element that accepts all style props from styled-system.
const SystemTextArea = system( const SystemTextArea = system(
@ -34,6 +34,12 @@ const SystemTextArea = system(
class TextArea extends React.PureComponent { class TextArea extends React.PureComponent {
static displayName = 'TextArea' static displayName = 'TextArea'
static defaultProps = {
description: null,
label: null,
showMessage: true
}
state = { state = {
hasFocus: false hasFocus: false
} }
@ -47,14 +53,19 @@ class TextArea extends React.PureComponent {
render() { render() {
const { const {
css, css,
description,
onChange, onChange,
onBlur, onBlur,
onFocus, onFocus,
forwardedRef, forwardedRef,
label,
required,
theme, theme,
field,
fieldApi, fieldApi,
fieldState, fieldState,
justifyContent, justifyContent,
showMessage,
...rest ...rest
} = this.props } = this.props
const { readOnly } = this.props const { readOnly } = this.props
@ -67,14 +78,34 @@ class TextArea extends React.PureComponent {
let borderColor let borderColor
if (readOnly) { if (readOnly) {
borderColor = theme.colors.gray borderColor = theme.colors.gray
} else if (fieldState.error) { } else if (fieldState.error || fieldState.asyncError) {
borderColor = theme.colors.superRed borderColor = theme.colors.superRed
} else if (value && !fieldState.error) { } else if (value && (!fieldState.error && !fieldState.asyncError)) {
borderColor = theme.colors.superGreen borderColor = theme.colors.superGreen
} }
// Extract any styled-system space props so that we can apply them directly to the wrapper.
const spaceProps = {}
Object.keys(rest).forEach(key => {
if ([...Object.keys(styles.space.propTypes), 'width'].includes(key)) {
spaceProps[key] = rest[key]
delete rest[key]
}
})
return ( return (
<Flex flexDirection="column" justifyContent={justifyContent}> <Flex flexDirection="column" justifyContent={justifyContent} {...spaceProps}>
{label && (
<Label htmlFor={field} mb={2}>
{label}
{required && (
<Span fontSize="s" css={{ 'vertical-align': 'super' }}>
{' '}
*
</Span>
)}
</Label>
)}
<SystemTextArea <SystemTextArea
borderColor={borderColor || theme.colors.gray} borderColor={borderColor || theme.colors.gray}
opacity={readOnly ? 0.6 : null} opacity={readOnly ? 0.6 : null}
@ -90,6 +121,7 @@ class TextArea extends React.PureComponent {
css css
)} )}
{...rest} {...rest}
field={field}
ref={this.inputRef} ref={this.inputRef}
value={!value && value !== 0 ? '' : value} value={!value && value !== 0 ? '' : value}
onChange={e => { onChange={e => {
@ -119,13 +151,27 @@ class TextArea extends React.PureComponent {
onFocus(e) onFocus(e)
} }
}} }}
required={required}
error={fieldState.error} error={fieldState.error}
/> />
{fieldState.error && ( <Flex>
<Message variant={hasFocus ? 'warning' : 'error'} justifyContent={justifyContent} mt={2}> {description && (
{fieldState.error} <Text color="gray" fontSize="s" mt={1} mr="auto">
</Message> {description}
)} </Text>
)}
{showMessage &&
(fieldState.error || fieldState.asyncError) && (
<Message
variant={hasFocus ? 'warning' : 'error'}
justifyContent={justifyContent}
mt={1}
ml="auto"
>
{fieldState.error || fieldState.asyncError}
</Message>
)}
</Flex>
</Flex> </Flex>
) )
} }

12
app/components/UI/Truncate.js

@ -1,20 +1,22 @@
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
const Truncate = ({ text, maxlen = 12 }) => { const Truncate = ({ text, maxlen = 12 }) => {
if (!text) { if (text === null || typeof text === 'undefined' || text === '') {
return null return null
} }
const textString = text.toString()
const truncatedText = const truncatedText =
text.length < maxlen textString.length < maxlen
? text ? textString
: text.substr(0, maxlen / 2) + '...' + text.substr(text.length - maxlen / 2) : textString.substr(0, maxlen / 2) + '...' + textString.substr(textString.length - maxlen / 2)
return truncatedText return truncatedText
} }
Truncate.propTypes = { Truncate.propTypes = {
text: PropTypes.string.isRequired, text: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
maxlen: PropTypes.number maxlen: PropTypes.number
} }

2
app/components/UI/index.js

@ -25,12 +25,14 @@ export Modal from './Modal'
export Notification from './Notification' export Notification from './Notification'
export Panel, { PanelHeader, PanelBody, PanelFooter } from './Panel' export Panel, { PanelHeader, PanelBody, PanelFooter } from './Panel'
export Page from './Page' export Page from './Page'
export PasswordInput from './PasswordInput'
export QRCode from './QRCode' export QRCode from './QRCode'
export Radio from './Radio' export Radio from './Radio'
export RadioGroup from './RadioGroup' export RadioGroup from './RadioGroup'
export Range from './Range' export Range from './Range'
export Select from './Select' export Select from './Select'
export Sidebar from './Sidebar' export Sidebar from './Sidebar'
export Span from './Span'
export Spinner from './Spinner' export Spinner from './Spinner'
export Text from './Text' export Text from './Text'
export TextArea from './TextArea' export TextArea from './TextArea'

109
stories/forms/form.stories.js

@ -2,12 +2,12 @@ import React from 'react'
import { storiesOf } from '@storybook/react' import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions' import { action } from '@storybook/addon-actions'
import { Box } from 'rebass' import { Box } from 'rebass'
import { Form } from 'informed'
import { import {
CryptoAmountInput, CryptoAmountInput,
FiatAmountInput, FiatAmountInput,
Page, Page,
MainContent, MainContent,
Form,
Input, Input,
Label, Label,
LightningInvoiceInput, LightningInvoiceInput,
@ -35,9 +35,22 @@ const selectItems = [
storiesOf('Forms', module) storiesOf('Forms', module)
.add('Input', () => ( .add('Input', () => (
<Form> <>
<Input field="fieldName" id="field-name" /> <Form mb={4}>
</Form> <Input field="fieldName" id="field-name" />
</Form>
<Form mb={4}>
<Input field="fieldName" id="field-name" label="Field with Label" />
</Form>
<Form mb={4}>
<Input
field="fieldName"
id="field-name"
label="Field with Label and description"
description="This field also has a description."
/>
</Form>
</>
)) ))
.add('Label', () => ( .add('Label', () => (
<Form> <Form>
@ -52,64 +65,48 @@ storiesOf('Forms', module)
.add('CryptoAmountInput', () => ( .add('CryptoAmountInput', () => (
<Form> <Form>
<Box my={4}> <Box my={4}>
<Box> <CryptoAmountInput field="cryptoBtc" currency="btc" width={150} label="BTC" />
<Label htmlFor="cryptoBtc">BTC</Label>
</Box>
<CryptoAmountInput field="cryptoBtc" currency="btc" width={150} />
</Box> </Box>
<Box my={4}> <Box my={4}>
<Box> <CryptoAmountInput field="cryptoBits" currency="bits" width={150} label="Bits" />
<Label htmlFor="cryptoBits">Bits</Label>
</Box>
<CryptoAmountInput field="cryptoBits" currency="bits" width={150} />
</Box> </Box>
<Box my={4}> <Box my={4}>
<Box> <CryptoAmountInput field="cryptoSats" currency="sats" width={150} label="Sats" />
<Label htmlFor="cryptoSats">Sats</Label>
</Box>
<CryptoAmountInput field="cryptoSats" currency="sats" width={150} />
</Box> </Box>
</Form> </Form>
)) ))
.add('FiatAmountInput', () => ( .add('FiatAmountInput', () => (
<Form> <Form>
<Box my={4}> <Box my={4}>
<Box> <FiatAmountInput field="fiat" currency="usd" width={150} label="USD" />
<Label htmlFor="fiat">USD</Label>
</Box>
<FiatAmountInput field="fiat" currency="usd" width={150} />
</Box> </Box>
</Form> </Form>
)) ))
.add('Lightning Invoice Textarea', () => ( .add('Lightning Invoice Textarea', () => (
<React.Fragment> <React.Fragment>
<Box my={4}> <Box my={4}>
<Box>
<Label htmlFor="testnet">Bitcoin or Lightning address (testnet)</Label>
</Box>
<Form id="testnet"> <Form id="testnet">
<LightningInvoiceInput <LightningInvoiceInput
chain="bitcoin" chain="bitcoin"
network="testnet" network="testnet"
field="testnet" field="testnet"
id="testnet" id="testnet"
label="Bitcoin or Lightning address (testnet)"
validateOnBlur validateOnBlur
validateOnChange validateOnChange
/> />
</Form> </Form>
</Box> </Box>
<Box> <Box>
<Box>
<Label htmlFor="mainnet">Bitcoin or Lightning address (mainnet)</Label>
</Box>
<Form id="testnet"> <Form id="testnet">
<LightningInvoiceInput <LightningInvoiceInput
chain="bitcoin" chain="bitcoin"
network="mainnet" network="mainnet"
field="mainnet" field="mainnet"
id="mainnet" id="mainnet"
label="Bitcoin or Lightning address (mainnet)"
validateOnBlur validateOnBlur
validateOnChange validateOnChange
/> />
@ -148,56 +145,44 @@ storiesOf('Forms', module)
{({ formState }) => ( {({ formState }) => (
<React.Fragment> <React.Fragment>
<Box my={4}> <Box my={4}>
<Box> <Input
<Label htmlFor="input1">Example Field</Label> field="input1"
</Box> id="field-name"
<Box> label="Example Field"
<Input placeholder="Type here"
field="input1" validate={validate}
id="field-name" validateOnBlur
placeholder="Type here" />
validate={validate}
validateOnBlur
/>
</Box>
</Box> </Box>
<Box my={4}> <Box my={4}>
<Box> <TextArea
<Label htmlFor="textarea1">Example Textarea</Label> field="textarea1"
</Box> placeholder="Type here"
<Box> label="Example TextArea"
<TextArea validate={validate}
field="textarea1" validateOnBlur
placeholder="Type here" />
validate={validate}
validateOnBlur
/>
</Box>
</Box> </Box>
<Box my={4}> <Box my={4}>
<Box>
<Label htmlFor="testnet">Bitcoin or Lightning address (testnet)</Label>
</Box>
<LightningInvoiceInput <LightningInvoiceInput
chain="bitcoin" chain="bitcoin"
network="testnet" network="testnet"
field="testnet" field="testnet"
id="testnet" id="testnet"
label="Bitcoin or Lightning address (testnet)"
validateOnBlur validateOnBlur
validateOnChange validateOnChange
/> />
</Box> </Box>
<Box> <Box>
<Box>
<Label htmlFor="mainnet">Bitcoin or Lightning address (mainnet)</Label>
</Box>
<LightningInvoiceInput <LightningInvoiceInput
chain="bitcoin" chain="bitcoin"
network="mainnet" network="mainnet"
field="mainnet" field="mainnet"
id="mainnet" id="mainnet"
label="Bitcoin or Lightning address (mainnet)"
validateOnBlur validateOnBlur
validateOnChange validateOnChange
/> />
@ -205,7 +190,9 @@ storiesOf('Forms', module)
<Box my={4}> <Box my={4}>
<Box> <Box>
<Label htmlFor="selectfield1">Example Select</Label> <Label htmlFor="selectfield1" mb={2}>
Example Select
</Label>
</Box> </Box>
<Box> <Box>
<Select <Select
@ -228,16 +215,20 @@ storiesOf('Forms', module)
<Box my={4}> <Box my={4}>
<Box> <Box>
<Label htmlFor="checkbox1">Example Toggle</Label> <Label htmlFor="checkbox1" mb={2}>
Example Toggle
</Label>
</Box> </Box>
<Box> <Box>
<Toggle field="checkbox1" onChange={action('change')} /> <Toggle field="checkbox1" onChange={action('change')} label="Example Toggle" />
</Box> </Box>
</Box> </Box>
<Box my={4}> <Box my={4}>
<Box> <Box>
<Label htmlFor="slider1">Example Range</Label> <Label htmlFor="slider1" mb={2}>
Example Range
</Label>
</Box> </Box>
<Box> <Box>
<Range field="slider1" initialValue={25} onChange={action('change')} /> <Range field="slider1" initialValue={25} onChange={action('change')} />

3
test/unit/components/Pay/__snapshots__/PayHeader.spec.js.snap

@ -2,6 +2,7 @@
exports[`component.Pay.PayHeader generic should render correctly 1`] = ` exports[`component.Pay.PayHeader generic should render correctly 1`] = `
<Header <Header
align="center"
logo={ logo={
<SvgPaperPlane <SvgPaperPlane
height="38px" height="38px"
@ -19,6 +20,7 @@ exports[`component.Pay.PayHeader generic should render correctly 1`] = `
exports[`component.Pay.PayHeader offchain should render correctly 1`] = ` exports[`component.Pay.PayHeader offchain should render correctly 1`] = `
<Header <Header
align="center"
logo={ logo={
<SvgLightning <SvgLightning
height="45px" height="45px"
@ -38,6 +40,7 @@ exports[`component.Pay.PayHeader offchain should render correctly 1`] = `
exports[`component.Pay.PayHeader onchain should render correctly 1`] = ` exports[`component.Pay.PayHeader onchain should render correctly 1`] = `
<Header <Header
align="center"
logo={ logo={
<SvgOnchain <SvgOnchain
height="45px" height="45px"

5
test/unit/components/Pay/__snapshots__/PaySummaryRow.spec.js.snap

@ -3,17 +3,18 @@
exports[`component.Form.DataRow should render correctly 1`] = ` exports[`component.Form.DataRow should render correctly 1`] = `
<Styled(styled.div) <Styled(styled.div)
alignItems="center" alignItems="center"
justifyContent="space-between"
py={3} py={3}
> >
<Text <Text
fontWeight="normal" fontWeight="normal"
width={0.5} width={0.4}
> >
left contnet left contnet
</Text> </Text>
<Text <Text
textAlign="right" textAlign="right"
width={0.5} width={0.6}
> >
right content right content
</Text> </Text>

75
test/unit/components/UI/__snapshots__/Header.spec.js.snap

@ -5,6 +5,7 @@ exports[`component.UI.Header should render correctly with default props 1`] = `
alignItems="center" alignItems="center"
as="header" as="header"
flexDirection="column" flexDirection="column"
justifyContent="center"
/> />
`; `;
@ -13,23 +14,20 @@ exports[`component.UI.Header should render correctly with logo 1`] = `
alignItems="center" alignItems="center"
as="header" as="header"
flexDirection="column" flexDirection="column"
justifyContent="center"
> >
<Styled(styled.div) <Text
alignItems="center" css={
justifyContent="center" Object {
> "height": "50px",
<Text
css={
Object {
"height": "50px",
}
} }
fontSize="50px" }
lineHeight="1em" fontSize="50px"
> lineHeight="1em"
logo here textAlign="center"
</Text> >
</Styled(styled.div)> logo here
</Text>
</Styled(styled.div)> </Styled(styled.div)>
`; `;
@ -38,8 +36,11 @@ exports[`component.UI.Header should render correctly with subtitle 1`] = `
alignItems="center" alignItems="center"
as="header" as="header"
flexDirection="column" flexDirection="column"
justifyContent="center"
> >
<Heading4> <Heading4
textAlign="center"
>
subtitle here subtitle here
</Heading4> </Heading4>
</Styled(styled.div)> </Styled(styled.div)>
@ -50,8 +51,12 @@ exports[`component.UI.Header should render correctly with title 1`] = `
alignItems="center" alignItems="center"
as="header" as="header"
flexDirection="column" flexDirection="column"
justifyContent="center"
> >
<Heading1> <Heading1
mb={1}
textAlign="center"
>
title here title here
</Heading1> </Heading1>
</Styled(styled.div)> </Styled(styled.div)>
@ -62,27 +67,29 @@ exports[`component.UI.Header should render correctly with title, subtitle, and l
alignItems="center" alignItems="center"
as="header" as="header"
flexDirection="column" flexDirection="column"
justifyContent="center"
> >
<Styled(styled.div) <Text
alignItems="center" css={
justifyContent="center" Object {
> "height": "50px",
<Text
css={
Object {
"height": "50px",
}
} }
fontSize="50px" }
lineHeight="1em" fontSize="50px"
> lineHeight="1em"
logo here textAlign="center"
</Text> >
</Styled(styled.div)> logo here
<Heading1> </Text>
<Heading1
mb={1}
textAlign="center"
>
title here title here
</Heading1> </Heading1>
<Heading4> <Heading4
textAlign="center"
>
logo here logo here
</Heading4> </Heading4>
</Styled(styled.div)> </Styled(styled.div)>

2
test/unit/components/UI/__snapshots__/Heading.spec.js.snap

@ -6,7 +6,7 @@ exports[`component.UI.Heading should render correctly 1`] = `
font-size: 32px; font-size: 32px;
color: primaryText; color: primaryText;
font-weight: light; font-weight: light;
line-height: 1.4; line-height: 1.2;
} }
<h2 <h2

11
test/unit/components/UI/__snapshots__/Input.spec.js.snap

@ -13,18 +13,15 @@ exports[`component.UI.Input should render correctly 1`] = `
.c1 { .c1 {
padding: 16px; padding: 16px;
width: 100%;
font-size: 13px;
color: #ffffff;
background-color: transparent;
color: #ffffff; color: #ffffff;
background-color: transparent; background-color: transparent;
font-family: Roboto,system-ui,sans-serif;
font-weight: 300;
border: 1px solid;
border: 1px solid; border: 1px solid;
border-color: #959595; border-color: #959595;
border-radius: 5px; border-radius: 5px;
font-family: Roboto,system-ui,sans-serif;
font-size: 13px;
font-weight: 300;
width: 100%;
outline: none; outline: none;
} }

10
test/unit/components/UI/__snapshots__/LightningInvoiceInput.spec.js.snap

@ -11,6 +11,13 @@ exports[`component.UI.LightningInvoiceInput should render correctly 1`] = `
flex-direction: column; flex-direction: column;
} }
.c2 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.c1 { .c1 {
padding: 16px; padding: 16px;
width: 100%; width: 100%;
@ -56,6 +63,9 @@ exports[`component.UI.LightningInvoiceInput should render correctly 1`] = `
value="" value=""
width={1} width={1}
/> />
<div
className="c2"
/>
</div> </div>
</form> </form>
`; `;

2
test/unit/components/UI/__snapshots__/MainContent.spec.js.snap

@ -5,6 +5,8 @@ exports[`component.UI.MainContent should render correctly 1`] = `
width: 100%; width: 100%;
color: primaryText; color: primaryText;
background-color: primaryColor; background-color: primaryColor;
position: relative;
overflow-y: scroll;
} }
<article <article

26
test/unit/components/UI/__snapshots__/Message.spec.js.snap

@ -1,17 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`component.UI.Message should render correctly with default props 1`] = ` exports[`component.UI.Message should render correctly with default props 1`] = `
<Styled(Styled(styled.div)) <Styled(styled.div)
alignItems="center" fontSize="s"
fontWeight="normal"
> >
<styled.div <Styled(Styled(styled.div))
mr={1} alignItems="center"
/>
<Styled(styled.div)
fontSize="s"
fontWeight="normal"
> >
<styled.div
css={
Object {
"height": "14px",
}
}
mr={1}
width="14px"
/>
A message A message
</Styled(styled.div)> </Styled(Styled(styled.div))>
</Styled(Styled(styled.div))> </Styled(styled.div)>
`; `;

87
test/unit/components/UI/__snapshots__/Modal.spec.js.snap

@ -1,24 +1,34 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`component.UI.Modal should render correctly 1`] = ` exports[`component.UI.Modal should render correctly 1`] = `
.c2 { .c1 {
padding: 16px;
}
.c3 {
margin-left: auto; margin-left: auto;
padding: 8px; padding: 8px;
height: 40px;
cursor: pointer; cursor: pointer;
opacity: 0.6;
}
.c3:hover {
opacity: 1; opacity: 1;
} }
.c3 { .c4 {
padding: 16px; padding-bottom: 32px;
padding-top: 4px; padding-left: 32px;
padding-right: 32px;
-webkit-flex: 1; -webkit-flex: 1;
-ms-flex: 1; -ms-flex: 1;
flex: 1; flex: 1;
} }
.c0 { .c0 {
padding: 16px;
width: 100%; width: 100%;
color: primaryText;
background-color: primaryColor; background-color: primaryColor;
height: 100%; height: 100%;
display: -webkit-box; display: -webkit-box;
@ -30,56 +40,59 @@ exports[`component.UI.Modal should render correctly 1`] = `
flex-direction: column; flex-direction: column;
} }
.c1 { .c2 {
color: primaryText;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
display: -ms-flexbox; display: -ms-flexbox;
display: flex; display: flex;
-webkit-box-pack: end; -webkit-box-pack: justify;
-webkit-justify-content: flex-end; -webkit-justify-content: space-between;
-ms-flex-pack: end; -ms-flex-pack: justify;
justify-content: flex-end; justify-content: space-between;
} }
<div <article
className="c0" className="c0"
color="primaryText"
width={1} width={1}
> >
<header <header
className="c1" className="c1"
color="primaryText"
> >
<div <div
className="c2" className="c2"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
> >
<svg <div
height="2em" className="c3"
viewBox="0 0 22 20"
width="2em"
> >
<defs> <svg
<path height={20}
d="M10 10L0 20l10-10L0 0l10 10L20 0l-8.394 8.394L10 10l1.606 1.606L20 20 10 10z" viewBox="0 0 22 20"
id="x_svg__a" width={20}
>
<defs>
<path
d="M10 10L0 20l10-10L0 0l10 10L20 0l-8.394 8.394L10 10l1.606 1.606L20 20 10 10z"
id="x_svg__a"
/>
</defs>
<use
fill="none"
fillRule="evenodd"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
transform="translate(1)"
xlinkHref="#x_svg__a"
/> />
</defs> </svg>
<use </div>
fill="none"
fillRule="evenodd"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
transform="translate(1)"
xlinkHref="#x_svg__a"
/>
</svg>
</div> </div>
</header> </header>
<section <section
className="c3" className="c4"
/> >
</div>
</section>
</article>
`; `;

2
test/unit/components/UI/__snapshots__/Page.spec.js.snap

@ -4,9 +4,9 @@ exports[`component.UI.Page should render correctly 1`] = `
.c0 { .c0 {
background-color: primaryColor; background-color: primaryColor;
height: 100%; height: 100%;
overflow: hidden;
min-width: 950px; min-width: 950px;
min-height: 600px; min-height: 600px;
overflow-y: hidden;
box-shadow: 0 20px 70px rgba(0,0,0,0.55); box-shadow: 0 20px 70px rgba(0,0,0,0.55);
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;

4
test/unit/components/UI/__snapshots__/Radio.spec.js.snap

@ -16,7 +16,6 @@ exports[`component.UI.Radio should render correctly 1`] = `
.c1 { .c1 {
margin-bottom: 16px; margin-bottom: 16px;
width: 100%;
font-size: 13px; font-size: 13px;
color: #ffffff; color: #ffffff;
color: #ffffff; color: #ffffff;
@ -94,7 +93,6 @@ exports[`component.UI.Radio should render correctly 1`] = `
fontWeight="light" fontWeight="light"
htmlFor="item1" htmlFor="item1"
opacity={null} opacity={null}
width={1}
> >
<div <div
className="c2" className="c2"
@ -134,7 +132,6 @@ exports[`component.UI.Radio should render correctly 1`] = `
fontWeight="light" fontWeight="light"
htmlFor="item2" htmlFor="item2"
opacity={null} opacity={null}
width={1}
> >
<div <div
className="c2" className="c2"
@ -174,7 +171,6 @@ exports[`component.UI.Radio should render correctly 1`] = `
fontWeight="light" fontWeight="light"
htmlFor="item3" htmlFor="item3"
opacity={null} opacity={null}
width={1}
> >
<div <div
className="c2" className="c2"

11
test/unit/components/UI/__snapshots__/Select.spec.js.snap

@ -13,18 +13,15 @@ exports[`component.UI.Toggle should render correctly 1`] = `
.c2 { .c2 {
padding: 16px; padding: 16px;
width: 100%;
font-size: 13px;
color: #ffffff;
background-color: transparent;
color: #ffffff; color: #ffffff;
background-color: transparent; background-color: transparent;
font-family: Roboto,system-ui,sans-serif;
font-weight: 300;
border: 1px solid;
border: 1px solid; border: 1px solid;
border-color: #959595; border-color: #959595;
border-radius: 5px; border-radius: 5px;
font-family: Roboto,system-ui,sans-serif;
font-size: 13px;
font-weight: 300;
width: 100%;
outline: none; outline: none;
} }

2
test/unit/components/UI/__snapshots__/Sidebar.spec.js.snap

@ -5,6 +5,8 @@ exports[`component.UI.Sidebar should render correctly 1`] = `
width: 33.33333333333333%; width: 33.33333333333333%;
color: primaryText; color: primaryText;
background-color: tertiaryColor; background-color: tertiaryColor;
position: relative;
overflow-y: scroll;
} }
<aside <aside

11
test/unit/components/UI/__snapshots__/TextArea.spec.js.snap

@ -13,18 +13,15 @@ exports[`component.UI.Input should render correctly 1`] = `
.c1 { .c1 {
padding: 16px; padding: 16px;
width: 100%;
font-size: 13px;
color: #ffffff;
background-color: transparent;
color: #ffffff; color: #ffffff;
background-color: transparent; background-color: transparent;
font-family: Roboto,system-ui,sans-serif;
font-weight: 300;
border: 1px solid;
border: 1px solid; border: 1px solid;
border-color: #959595; border-color: #959595;
border-radius: 5px; border-radius: 5px;
font-family: Roboto,system-ui,sans-serif;
font-size: 13px;
font-weight: 300;
width: 100%;
outline: none; outline: none;
} }

Loading…
Cancel
Save