diff --git a/packages/ui-toolkit/src/breakpoints/index.js b/packages/ui-toolkit/src/breakpoints/index.js index 043ba650..b9d4fede 100644 --- a/packages/ui-toolkit/src/breakpoints/index.js +++ b/packages/ui-toolkit/src/breakpoints/index.js @@ -1,40 +1,42 @@ import { css } from 'styled-components'; import remcalc from 'remcalc'; -const bounds = { +export const breakpoints = { small: { - upper: remcalc(768) + upper: 768 }, medium: { - upper: remcalc(1024), - lower: remcalc(769) + upper: 1024, + lower: 769 }, large: { - upper: remcalc(1200), - lower: remcalc(1025) + upper: 1200, + lower: 1025 }, xlarge: { - lower: remcalc(1201) + lower: 1201 } }; const screens = { // >= 768px - smallOnly: `only screen and (max-width: ${bounds.small.upper})`, - small: `only screen and (min-width: ${bounds.small.upper}})`, + smallOnly: `only screen and (max-width: ${remcalc(breakpoints.small.upper)})`, + small: `only screen and (min-width: ${remcalc(breakpoints.small.upper)})`, // >= 1024px - mediumOnly: `only screen and (min-width: ${bounds.medium.lower}) - and (max-width: ${bounds.medium.upper})`, - mediumDown: `only screen and (max-width: ${bounds.medium.upper})`, - medium: `only screen and (min-width: ${bounds.medium.lower})`, + mediumOnly: `only screen and (min-width: ${remcalc(breakpoints.medium.lower)}) + and (max-width: ${remcalc(breakpoints.medium.upper)})`, + mediumDown: `only screen and (max-width: ${remcalc( + breakpoints.medium.upper + )})`, + medium: `only screen and (min-width: ${remcalc(breakpoints.medium.lower)})`, // >= 1200px - largeOnly: `only screen and (min-width: ${bounds.large.lower}) - and (max-width: ${bounds.large.upper})`, - largeDown: `only screen and (max-width: ${bounds.large.upper})`, - large: `only screen and (min-width: ${bounds.large.upper})`, - xlarge: `only screen and (min-width: ${bounds.xlarge.lower}) - and (max-width: ${bounds.xlarge.upper})`, - xlargeUp: `only screen and (min-width: ${bounds.xlarge.lower})` + largeOnly: `only screen and (min-width: ${remcalc(breakpoints.large.lower)}) + and (max-width: ${remcalc(breakpoints.large.upper)})`, + largeDown: `only screen and (max-width: ${remcalc(breakpoints.large.upper)})`, + large: `only screen and (min-width: ${remcalc(breakpoints.large.upper)})`, + xlarge: `only screen and (min-width: ${remcalc(breakpoints.xlarge.lower)}) + and (max-width: ${remcalc(breakpoints.xlarge.upper)})`, + xlargeUp: `only screen and (min-width: ${remcalc(breakpoints.xlarge.lower)})` }; const breakpoint = label => (...args) => css` @@ -64,5 +66,6 @@ export default { largeDown, large, xlarge, - xlargeUp + xlargeUp, + breakpoints }; diff --git a/packages/ui-toolkit/src/dropdown/index.js b/packages/ui-toolkit/src/dropdown/index.js new file mode 100644 index 00000000..e32d12b5 --- /dev/null +++ b/packages/ui-toolkit/src/dropdown/index.js @@ -0,0 +1,131 @@ +import React, { Component } from 'react'; +import ReactDOM from 'react-dom'; +import PropTypes from 'prop-types'; +import styled from 'styled-components'; +import { Select } from '../form'; +import { Tooltip, TooltipButton as DropdownItem } from '../tooltip'; +import Baseline from '../baseline'; +import { small, smallOnly } from '../breakpoints'; +import { ArrowIcon } from '../icons'; +import remcalc from 'remcalc'; + +const StyledSelectList = styled(Tooltip)` + ${smallOnly` + display: none; + `}; + width: 100%; + ul { + position: relative; + display: block; + left: auto; + } + ul:after, ul:before { + left: 97%; + } +`; + +const StyledSelect = styled(Select)` + ${small` + option { + display: none; + } + `} +`; + +const StyledArrowIcon = styled(ArrowIcon)` + ${smallOnly` + display: none; + `}; + position: absolute; + left: 97%; + top: 50%; + margin-left: -${remcalc(4.5)}; +`; + +const Container = styled.div` + position: relative; +`; + +/** + * @example ./usage.md + */ +class Dropdown extends Component { + constructor(props) { + super(props); + + this.toggleDropdown = this.toggleDropdown.bind(this); + this.dropdownOnChange = this.dropdownOnChange.bind(this); + this.dropdownOnBlur = this.dropdownOnBlur.bind(this); + + this.state = { + isDroppedDown: false + }; + } + componentDidMount() { + window.addEventListener('click', this.dropdownOnBlur); + } + componentWillUnmount() { + window.addEventListener('click', this.dropdownOnBlur); + } + toggleDropdown() { + this.setState(prevState => ({ isDroppedDown: !prevState.isDroppedDown })); + } + dropdownOnBlur(ev) { + if ( + !ReactDOM.findDOMNode(this).contains(ev.target) && + this.state.isDroppedDown + ) { + this.toggleDropdown(); + } + } + dropdownOnChange(ev) { + this.setState({ isDroppedDown: false }); + if (!this.props.onChange) { + return; + } + this.props.onChange(ev.target.value); + } + render() { + const { data, placeholder, className, id, ...rest } = this.props; + return ( + + + + {data.map((val, index) => + + )} + + + {this.state.isDroppedDown && + + {data.map((val, index) => + + {val} + + )} + } + + ); + } +} + +Dropdown.propTypes = { + data: PropTypes.arrayOf(PropTypes.string), + placeholder: PropTypes.string, + onChange: PropTypes.func +}; + +Dropdown.defaultProps = { + placeholder: 'Choose' +}; + +export default Baseline(Dropdown); diff --git a/packages/ui-toolkit/src/dropdown/usage.md b/packages/ui-toolkit/src/dropdown/usage.md new file mode 100644 index 00000000..51a6e05d --- /dev/null +++ b/packages/ui-toolkit/src/dropdown/usage.md @@ -0,0 +1,10 @@ +``` +const Dropdown = require('./index').default; +const Label = require('../form/label').default; + +
+ + + +
+``` diff --git a/packages/ui-toolkit/src/icons/arrow.js b/packages/ui-toolkit/src/icons/arrow.js new file mode 100644 index 00000000..64bb5e42 --- /dev/null +++ b/packages/ui-toolkit/src/icons/arrow.js @@ -0,0 +1,7 @@ +import Baseline from '../baseline'; +// eslint-disable-next-line no-unused-vars +import React from 'react'; + +import ArrowIcon from './svg/icon_arrow.svg'; + +export default Baseline(ArrowIcon); diff --git a/packages/ui-toolkit/src/icons/index.js b/packages/ui-toolkit/src/icons/index.js index 2ae28cc3..d94712c5 100644 --- a/packages/ui-toolkit/src/icons/index.js +++ b/packages/ui-toolkit/src/icons/index.js @@ -1,3 +1,4 @@ export { default as CloseIcon } from './close'; export { default as PlusIcon } from './plus'; export { default as MinusIcon } from './minus'; +export { default as ArrowIcon } from './arrow'; diff --git a/packages/ui-toolkit/src/index.js b/packages/ui-toolkit/src/index.js index 5e7dcc86..d6249603 100644 --- a/packages/ui-toolkit/src/index.js +++ b/packages/ui-toolkit/src/index.js @@ -18,6 +18,7 @@ export { default as CloseButton } from './close-button'; export { default as IconButton } from './icon-button'; export { Tooltip, TooltipButton, TooltipDivider } from './tooltip'; +export { Dropdown } from './dropdown'; export { borderRadius, diff --git a/packages/ui-toolkit/src/tooltip/tooltip.js b/packages/ui-toolkit/src/tooltip/tooltip.js index 7fb6ebc0..349aeab3 100644 --- a/packages/ui-toolkit/src/tooltip/tooltip.js +++ b/packages/ui-toolkit/src/tooltip/tooltip.js @@ -88,11 +88,19 @@ class Tooltip extends Component { top = 'auto', left = 'auto', bottom = 'auto', - right = 'auto' + right = 'auto', + className, + ...rest } = this.props; - return ( - + {children} diff --git a/packages/ui-toolkit/styleguide.config.js b/packages/ui-toolkit/styleguide.config.js index 501115c3..96981a90 100644 --- a/packages/ui-toolkit/styleguide.config.js +++ b/packages/ui-toolkit/styleguide.config.js @@ -59,6 +59,7 @@ module.exports = { 'src/form/input.js', 'src/form/number-input.js', 'src/form/checkbox.js', + 'src/dropdown/index.js', 'src/form/radio.js', 'src/form/select.js', 'src/form/toggle.js',