diff --git a/docs/styleguide.config.js b/docs/styleguide.config.js index ce1a595518..40d569df6c 100644 --- a/docs/styleguide.config.js +++ b/docs/styleguide.config.js @@ -29,6 +29,7 @@ module.exports = { '../react/Checkbox/index.jsx', '../react/Field/index.jsx', '../react/Input/index.jsx', + '../react/InputGroup/index.jsx', '../react/Label/index.jsx', '../react/Radio/index.jsx', '../react/SelectBox/SelectBox.jsx', diff --git a/react/InputGroup/Readme.md b/react/InputGroup/Readme.md new file mode 100644 index 0000000000..0e29b03ea8 --- /dev/null +++ b/react/InputGroup/Readme.md @@ -0,0 +1,89 @@ +### InputGroup with an appended text + +``` +const { Bold } = require('../Text'); +const Input = require('../Input').default; +
+
+ text}> + + +
+
+``` + +### InputGroup with a prepended text + +``` +const { Bold } = require('../Text'); +const Input = require('../Input').default; +
+
+ text}> + + +
+
+``` + +### InputGroup with an appended input + +You will need to set a width to the side component, with a utility class for example. + +``` +const Input = require('../Input').default; +
+
+ }> + + +
+
+``` + +### InputGroup with an appended select + +You will need to set a width to the side component, with a utility class for example. + +``` +const SelectBox = require('../SelectBox').default; +const options = [ + { value: 'cozy.io', label: '.cozy.io' }, + { value: 'cozycloud.cc', label: '.cozycloud.cc' } +]; +
+
+ }> + + +
+
+``` + +### Full width InputGroup with an appended text + +``` +const { Bold } = require('../Text'); +const Input = require('../Input').default; +
+
+ text}> + + +
+
+``` + +### Errored InputGroup with an appended text + +``` +const { Bold } = require('../Text'); +const Input = require('../Input').default; +
+
+ text}> + + +
+
+``` diff --git a/react/InputGroup/index.jsx b/react/InputGroup/index.jsx new file mode 100644 index 0000000000..a4d144f082 --- /dev/null +++ b/react/InputGroup/index.jsx @@ -0,0 +1,57 @@ +import React, { Component } from 'react' +import cx from 'classnames' +import PropTypes from 'prop-types' +import styles from './styles.styl' + +class InputGroup extends Component { + constructor(props) { + super(props) + this.handleFocus = this.handleFocus.bind(this) + this.handleBlur = this.handleBlur.bind(this) + this.state = { + focused: false + } + } + + handleFocus() { + this.setState({ focused: true }) + } + + handleBlur() { + this.setState({ focused: false }) + } + + render() { + const { children, prepend, append, error, fullwidth } = this.props + const { focused } = this.state + return ( +
+ {prepend && ( +
{prepend}
+ )} +
{children}
+ {append &&
{append}
} +
+ ) + } +} + +InputGroup.propTypes = { + prepend: PropTypes.object, + append: PropTypes.object, + error: PropTypes.bool, + fullwidth: PropTypes.bool +} + +InputGroup.defaultProps = { + error: false, + fullwidth: false +} + +export default InputGroup diff --git a/react/InputGroup/styles.styl b/react/InputGroup/styles.styl new file mode 100644 index 0000000000..97c36a5e44 --- /dev/null +++ b/react/InputGroup/styles.styl @@ -0,0 +1,22 @@ +@require '../../stylus/components/forms' + +.c-inputgroup + @extend $inputgroup + + input + @extend $inputgroup-input + +.c-inputgroup--focus + @extend $inputgroup--focus + +.c-inputgroup--error + @extend $inputgroup--error + +.c-inputgroup--fullwidth + @extend $inputgroup--fullwidth + +.c-inputgroup-main + @extend $inputgroup-main + +.c-inputgroup-side + @extend $inputgroup-side diff --git a/react/SelectBox/SelectBox.jsx b/react/SelectBox/SelectBox.jsx index bc7d0e2726..45b59d785f 100644 --- a/react/SelectBox/SelectBox.jsx +++ b/react/SelectBox/SelectBox.jsx @@ -7,7 +7,7 @@ import { isIOSApp } from 'cozy-device-helper' import styles from './styles.styl' import Icon from '../Icon' -import { dodgerBlue, silver, coolGrey } from '../palette' +import { dodgerBlue, silver, coolGrey, paleGrey } from '../palette' import withBreakpoints from '../helpers/withBreakpoints' const heights = { @@ -16,17 +16,24 @@ const heights = { large: '3rem' } +const borderStyle = (props, state) => { + if (props.inset) { + return '.125rem solid white' + } + return `.0625rem solid ${state.isFocused ? dodgerBlue : silver}` +} + const customStyles = props => ({ control: (base, state) => ({ ...base, // The gray background color is managed via the select--disabled // class applied below backgroundColor: props.disabled ? 'transparent' : 'white', - border: state.isFocused - ? `.0625rem solid ${dodgerBlue}` - : `.0625rem solid ${silver}`, + border: borderStyle(props, state), ':hover': { - borderColor: coolGrey + borderColor: props.inset ? 'white' : coolGrey, + backgroundColor: props.inset ? paleGrey : 'white', + cursor: 'pointer' }, borderRadius: '.1875rem', boxShadow: 'unset', @@ -335,6 +342,7 @@ SelectBox.propTypes = { components: PropTypes.object, disabled: PropTypes.bool, fullwidth: PropTypes.bool, + inset: PropTypes.bool, name: PropTypes.string, inputRef: PropTypes.func, size: PropTypes.oneOf(['tiny', 'medium', 'large']), @@ -344,6 +352,7 @@ SelectBox.propTypes = { SelectBox.defaultProps = { components: {}, fullwidth: false, + inset: false, size: 'large', styles: {} } diff --git a/react/__snapshots__/examples.spec.jsx.snap b/react/__snapshots__/examples.spec.jsx.snap index e3bfdcd123..2b1fe87c42 100644 --- a/react/__snapshots__/examples.spec.jsx.snap +++ b/react/__snapshots__/examples.spec.jsx.snap @@ -415,7 +415,7 @@ exports[`Field should render examples: Field 5`] = `
-
+
Select ...
@@ -434,7 +434,7 @@ exports[`Field should render examples: Field 5`] = `
-
+
Choice 2
@@ -3158,7 +3158,7 @@ exports[`SelectBox should render examples: SelectBox 1`] = ` "
-
+
Select...
@@ -3181,7 +3181,7 @@ exports[`SelectBox should render examples: SelectBox 1`] = ` exports[`SelectBox should render examples: SelectBox 2`] = ` "
-
+
Select...
@@ -3209,7 +3209,7 @@ exports[`SelectBox should render examples: SelectBox 3`] = ` "
Container height: 12rem.
-
+
Select...
@@ -3232,7 +3232,7 @@ exports[`SelectBox should render examples: SelectBox 3`] = ` exports[`SelectBox should render examples: SelectBox 4`] = ` "
-
+
Select...
@@ -3254,7 +3254,7 @@ exports[`SelectBox should render examples: SelectBox 4`] = ` exports[`SelectBox should render examples: SelectBox 5`] = ` "
-
+
Select...
@@ -3277,7 +3277,7 @@ exports[`SelectBox should render examples: SelectBox 6`] = ` "
-
+
Select...
@@ -3302,7 +3302,7 @@ exports[`SelectBox should render examples: SelectBox 7`] = `
-
+
I am a tiny SelectBox
@@ -3321,7 +3321,7 @@ exports[`SelectBox should render examples: SelectBox 7`] = `
-
+
I am a medium SelectBox
@@ -3340,7 +3340,7 @@ exports[`SelectBox should render examples: SelectBox 7`] = `
-
+
I am a large SelectBox
@@ -3388,7 +3388,7 @@ exports[`SelectBox should render examples: SelectBox 8`] = ` exports[`SelectBox should render examples: SelectBox 9`] = ` "
-
+
Select...
@@ -3410,7 +3410,7 @@ exports[`SelectBox should render examples: SelectBox 9`] = ` exports[`SelectBox should render examples: SelectBox 10`] = ` "
-
+
Select...
@@ -3432,7 +3432,7 @@ exports[`SelectBox should render examples: SelectBox 10`] = ` exports[`SelectBox should render examples: SelectBox 11`] = ` "
-
+
Select...
@@ -3492,7 +3492,7 @@ exports[`SelectBox should render examples: SelectBox 11`] = ` exports[`SelectBox should render examples: SelectBox 12`] = ` "
-
+
Select...
@@ -3514,7 +3514,7 @@ exports[`SelectBox should render examples: SelectBox 12`] = ` exports[`SelectBox should render examples: SelectBox 13`] = ` "
-
+
Select...
diff --git a/react/index.js b/react/index.js index 46ee479b41..63474ca784 100644 --- a/react/index.js +++ b/react/index.js @@ -76,3 +76,4 @@ export { default as FileInput } from './FileInput' export { default as Card } from './Card' export { default as InlineCard } from './InlineCard' export { default as PercentageLine } from './PercentageLine' +export { default as InputGroup } from './InputGroup' diff --git a/react/testFromStyleguidist.js b/react/testFromStyleguidist.js index e72ad8b1c9..43d42c27f5 100644 --- a/react/testFromStyleguidist.js +++ b/react/testFromStyleguidist.js @@ -33,6 +33,7 @@ import I18n from './I18n' import Icon from './Icon' import Infos from './Infos' import Input from './Input' +import InputGroup from './InputGroup' import IntentHeader from './IntentHeader' import IntentIframe from './IntentIframe' import IntentModal from './IntentModal' @@ -90,6 +91,7 @@ const testFromStyleguidist = (name, markdown, require) => { 'Icon', 'Infos', 'Input', + 'InputGroup', 'IntentHeader', 'IntentIframe', 'IntentModal', @@ -141,6 +143,7 @@ const testFromStyleguidist = (name, markdown, require) => { Icon, Infos, Input, + InputGroup, IntentHeader, IntentIframe, IntentModal, diff --git a/stylus/components/forms.styl b/stylus/components/forms.styl index 901c9212f2..5fdeca46aa 100644 --- a/stylus/components/forms.styl +++ b/stylus/components/forms.styl @@ -466,6 +466,49 @@ $field-inline flex-direction column margin-left 0 +$inputgroup + display inline-flex + flex-direction row + align-items stretch + width 100% + max-width rem(512) + border rem(1) solid var(--silver) + border-radius rem(2) + &:hover + border rem(1) solid var(--coolGrey) + +$inputgroup--focus + & + &:hover + border-color var(--dodgerBlue) + +$inputgroup--error + border-color var(--pomegranate) + +$inputgroup--fullwidth + max-width none + +$inputgroup-main + flex 1 1 auto + +$inputgroup-input + @extend $input-text + border 0 + padding-right rem(8) + &:hover + &:focus + position relative + z-index 1 + border 0 + outline 0 + +$inputgroup-side + display flex + flex-direction column + justify-content center + flex 0 1 auto + max-width rem(140) + $double-field width 100%