JimmyMow
6 years ago
committed by
GitHub
41 changed files with 632 additions and 1000 deletions
@ -0,0 +1,181 @@ |
|||
import React from 'react' |
|||
import PropTypes from 'prop-types' |
|||
import { Box, Flex } from 'rebass' |
|||
import styled, { withTheme } from 'styled-components' |
|||
import FaAngleDown from 'react-icons/lib/fa/angle-down' |
|||
import FaAngleUp from 'react-icons/lib/fa/angle-up' |
|||
import Check from 'components/Icon/Check' |
|||
import Text from 'components/UI/Text' |
|||
|
|||
/** |
|||
* Container |
|||
*/ |
|||
const DropdownContainer = styled(Flex)({}) |
|||
DropdownContainer.defaultProps = { |
|||
flexDirection: 'column', |
|||
flexWrap: 'none', |
|||
display: 'relative' |
|||
} |
|||
|
|||
/** |
|||
* Button |
|||
*/ |
|||
const DropdownButton = styled(Box)({ |
|||
appearance: 'none', |
|||
display: 'inline-block', |
|||
textAlign: 'center', |
|||
lineHeight: 'inherit', |
|||
textDecoration: 'none', |
|||
border: 'none', |
|||
outline: 'none', |
|||
background: 'transparent', |
|||
color: 'inherit', |
|||
cursor: 'pointer' |
|||
}) |
|||
DropdownButton.defaultProps = { |
|||
as: 'button', |
|||
m: 0, |
|||
px: 0, |
|||
py: 2, |
|||
textAlign: 'left' |
|||
} |
|||
|
|||
/** |
|||
* Menu |
|||
*/ |
|||
const MenuContainer = styled(Box)({ |
|||
display: 'relative' |
|||
}) |
|||
|
|||
const Menu = styled(Box)({ |
|||
cursor: 'pointer', |
|||
display: 'inline-block', |
|||
position: 'absolute', |
|||
'z-index': '999', |
|||
'min-width': '70px', |
|||
'list-style-type': 'none', |
|||
'border-radius': '3px', |
|||
'box-shadow': '0 3px 4px 0 rgba(30, 30, 30, 0.5)' |
|||
}) |
|||
Menu.defaultProps = { |
|||
as: 'ul', |
|||
m: 0, |
|||
p: 0, |
|||
bg: 'lightestBackground' |
|||
} |
|||
|
|||
/** |
|||
* MenuItem |
|||
*/ |
|||
const MenuItem = styled(Box)` |
|||
cursor: pointer; |
|||
&:hover { |
|||
background-color: ${props => props.theme.colors.darkestBackground}; |
|||
} |
|||
` |
|||
MenuItem.defaultProps = { |
|||
as: 'li', |
|||
px: 2, |
|||
py: 2 |
|||
} |
|||
|
|||
/** |
|||
* @render react |
|||
* @name Dropdown |
|||
* @example |
|||
* <Dropdown items={[ |
|||
* {name: 'Item 1', key: 'key1'}, |
|||
* {name: 'Item 2', key: 'key2'} |
|||
* ]} activeKey="key1" /> |
|||
*/ |
|||
class Dropdown extends React.Component { |
|||
state = { |
|||
isOpen: false |
|||
} |
|||
onChange = this.onChange.bind(this) |
|||
toggleMenu = this.toggleMenu.bind(this) |
|||
setWrapperRef = this.setWrapperRef.bind(this) |
|||
handleClickOutside = this.handleClickOutside.bind(this) |
|||
|
|||
static propTypes = { |
|||
activeKey: PropTypes.string.isRequired, |
|||
items: PropTypes.arrayOf( |
|||
PropTypes.shape({ |
|||
key: PropTypes.string.isRequired, |
|||
name: PropTypes.string.isRequired |
|||
}) |
|||
).isRequired, |
|||
onChange: PropTypes.func |
|||
} |
|||
|
|||
componentDidMount() { |
|||
document.addEventListener('mousedown', this.handleClickOutside) |
|||
} |
|||
|
|||
componentWillUnmount() { |
|||
document.removeEventListener('mousedown', this.handleClickOutside) |
|||
} |
|||
|
|||
onChange(key) { |
|||
const { onChange, activeKey } = this.props |
|||
if (key !== activeKey) { |
|||
if (onChange) { |
|||
onChange(key) |
|||
} |
|||
} |
|||
this.setState({ isOpen: false }) |
|||
} |
|||
|
|||
setWrapperRef(node) { |
|||
this.wrapperRef = node |
|||
} |
|||
|
|||
handleClickOutside(event) { |
|||
if (this.wrapperRef && !this.wrapperRef.contains(event.target)) { |
|||
this.setState({ isOpen: false }) |
|||
} |
|||
} |
|||
|
|||
toggleMenu() { |
|||
const { isOpen } = this.state |
|||
this.setState({ isOpen: !isOpen }) |
|||
} |
|||
|
|||
render() { |
|||
const { isOpen } = this.state |
|||
const { activeKey, items, theme, ...rest } = this.props |
|||
const selectedItem = items.find(c => c.key === activeKey) |
|||
return ( |
|||
<DropdownContainer ref={this.setWrapperRef} {...rest}> |
|||
<DropdownButton type="button" onClick={this.toggleMenu}> |
|||
<Text textAlign="left"> |
|||
{selectedItem ? selectedItem.name : activeKey}{' '} |
|||
{isOpen ? <FaAngleUp /> : <FaAngleDown />} |
|||
</Text> |
|||
</DropdownButton> |
|||
{isOpen && ( |
|||
<MenuContainer> |
|||
<Menu> |
|||
{items.map(item => { |
|||
return ( |
|||
<MenuItem key={item.key} onClick={() => this.onChange(item.key)}> |
|||
<Flex alignItems="center"> |
|||
<Text width="18px"> |
|||
{activeKey === item.key && ( |
|||
<Check height="0.95em" color={theme.colors.superGreen} /> |
|||
)} |
|||
</Text> |
|||
<Text>{item.name}</Text> |
|||
</Flex> |
|||
</MenuItem> |
|||
) |
|||
})} |
|||
</Menu> |
|||
</MenuContainer> |
|||
)} |
|||
</DropdownContainer> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default withTheme(Dropdown) |
@ -0,0 +1,58 @@ |
|||
import React from 'react' |
|||
import { storiesOf } from '@storybook/react' |
|||
import { StateDecorator, Store } from '@sambego/storybook-state' |
|||
import Dropdown from 'components/UI/Dropdown' |
|||
|
|||
const store = new Store({ |
|||
crypto: 'btc', |
|||
fiat: 'usd', |
|||
cryptoCurrencies: [ |
|||
{ |
|||
key: 'btc', |
|||
name: 'BTC' |
|||
}, |
|||
{ |
|||
key: 'bits', |
|||
name: 'bits' |
|||
}, |
|||
{ |
|||
key: 'sats', |
|||
name: 'satoshis' |
|||
} |
|||
], |
|||
fiatCurrencies: [ |
|||
{ |
|||
key: 'usd', |
|||
name: 'USD' |
|||
}, |
|||
{ |
|||
key: 'eur', |
|||
name: 'EUR' |
|||
}, |
|||
{ |
|||
key: 'gbp', |
|||
name: 'GBP' |
|||
} |
|||
] |
|||
}) |
|||
|
|||
storiesOf('Components.Dropdown', module) |
|||
.addDecorator(StateDecorator(store)) |
|||
.add('Crypto', () => { |
|||
return ( |
|||
<Dropdown |
|||
activeKey={store.get('crypto')} |
|||
items={store.get('cryptoCurrencies')} |
|||
onChange={crypto => store.set({ crypto })} |
|||
/> |
|||
) |
|||
}) |
|||
.add('Fiat', () => { |
|||
return ( |
|||
<Dropdown |
|||
activeKey={store.get('fiat')} |
|||
items={store.get('fiatCurrencies')} |
|||
onChange={fiat => store.set({ fiat })} |
|||
/> |
|||
) |
|||
}) |
@ -0,0 +1,30 @@ |
|||
import React from 'react' |
|||
import Dropdown from 'components/UI/Dropdown' |
|||
import renderer from 'react-test-renderer' |
|||
import { dark } from 'themes' |
|||
|
|||
const currencies = [ |
|||
{ |
|||
key: 'btc', |
|||
name: 'BTC' |
|||
}, |
|||
{ |
|||
key: 'bits', |
|||
name: 'bits' |
|||
}, |
|||
{ |
|||
key: 'sats', |
|||
name: 'satoshis' |
|||
} |
|||
] |
|||
|
|||
const setCurrency = jest.fn() |
|||
|
|||
describe('component.Dropdown', () => { |
|||
it('should render correctly', () => { |
|||
const tree = renderer |
|||
.create(<Dropdown theme={dark} activeKey="btc" items={currencies} onClick={setCurrency} />) |
|||
.toJSON() |
|||
expect(tree).toMatchSnapshot() |
|||
}) |
|||
}) |
@ -0,0 +1,81 @@ |
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP |
|||
|
|||
exports[`component.Dropdown should render correctly 1`] = ` |
|||
.c2 { |
|||
font-size: m; |
|||
text-align: left; |
|||
} |
|||
|
|||
.c0 { |
|||
display: -webkit-box; |
|||
display: -webkit-flex; |
|||
display: -ms-flexbox; |
|||
display: flex; |
|||
-webkit-flex-wrap: none; |
|||
-ms-flex-wrap: none; |
|||
flex-wrap: none; |
|||
-webkit-flex-direction: column; |
|||
-ms-flex-direction: column; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.c1 { |
|||
margin: 0px; |
|||
padding-left: 0px; |
|||
padding-right: 0px; |
|||
padding-top: 8px; |
|||
padding-bottom: 8px; |
|||
-webkit-appearance: none; |
|||
-moz-appearance: none; |
|||
appearance: none; |
|||
display: inline-block; |
|||
text-align: center; |
|||
line-height: inherit; |
|||
-webkit-text-decoration: none; |
|||
text-decoration: none; |
|||
border: none; |
|||
outline: none; |
|||
background: transparent; |
|||
color: inherit; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
<div |
|||
className="c0" |
|||
display="relative" |
|||
onClick={[MockFunction]} |
|||
> |
|||
<button |
|||
className="c1" |
|||
onClick={[Function]} |
|||
type="button" |
|||
> |
|||
<div |
|||
className="c2" |
|||
fontSize="m" |
|||
> |
|||
BTC |
|||
|
|||
<svg |
|||
fill="currentColor" |
|||
height="1em" |
|||
preserveAspectRatio="xMidYMid meet" |
|||
style={ |
|||
Object { |
|||
"color": undefined, |
|||
"verticalAlign": "middle", |
|||
} |
|||
} |
|||
viewBox="0 0 40 40" |
|||
width="1em" |
|||
> |
|||
<g> |
|||
<path |
|||
d="m31 16.4q0 0.3-0.2 0.5l-10.4 10.4q-0.3 0.3-0.5 0.3t-0.6-0.3l-10.4-10.4q-0.2-0.2-0.2-0.5t0.2-0.5l1.2-1.1q0.2-0.2 0.5-0.2t0.5 0.2l8.8 8.8 8.7-8.8q0.3-0.2 0.5-0.2t0.6 0.2l1.1 1.1q0.2 0.2 0.2 0.5z" |
|||
/> |
|||
</g> |
|||
</svg> |
|||
</div> |
|||
</button> |
|||
</div> |
|||
`; |
Loading…
Reference in new issue