refactor Input and Select
This commit is contained in:
parent
bd1ff8171a
commit
8ac2a7c60f
23
ui/src/components/form/label-row.js
Normal file
23
ui/src/components/form/label-row.js
Normal file
@ -0,0 +1,23 @@
|
||||
const Column = require('../column');
|
||||
const React = require('react');
|
||||
const Row = require('../row');
|
||||
|
||||
const LabelRow = (props) => {
|
||||
const labels = React.Children.map(props.children, (children) => (
|
||||
<Column md={6} xs={12}>
|
||||
{children}
|
||||
</Column>
|
||||
));
|
||||
|
||||
return (
|
||||
<Row>
|
||||
{labels}
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
LabelRow.propTypes = {
|
||||
children: React.PropTypes.node
|
||||
};
|
||||
|
||||
module.exports = LabelRow;
|
24
ui/src/components/form/label.js
Normal file
24
ui/src/components/form/label.js
Normal file
@ -0,0 +1,24 @@
|
||||
const constants = require('../../shared/constants');
|
||||
const fns = require('../../shared/functions');
|
||||
const Styled = require('styled-components');
|
||||
|
||||
const {
|
||||
colors
|
||||
} = constants;
|
||||
|
||||
const {
|
||||
remcalc
|
||||
} = fns;
|
||||
|
||||
const {
|
||||
default: styled
|
||||
} = Styled;
|
||||
|
||||
module.exports = styled.label`
|
||||
width: 100%;
|
||||
font-size: ${remcalc(16)};
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
font-stretch: normal;
|
||||
color: ${colors.brandSecondaryColor};
|
||||
`;
|
29
ui/src/components/form/msg.js
Normal file
29
ui/src/components/form/msg.js
Normal file
@ -0,0 +1,29 @@
|
||||
const constants = require('../../shared/constants');
|
||||
const Label = require('./label');
|
||||
const match = require('../../shared/match');
|
||||
const Styled = require('styled-components');
|
||||
|
||||
const {
|
||||
breakpoints,
|
||||
colors
|
||||
} = constants;
|
||||
|
||||
const {
|
||||
default: styled
|
||||
} = Styled;
|
||||
|
||||
const color = match.prop({
|
||||
warning: colors.inputWarning,
|
||||
error: colors.inputError,
|
||||
//disabled: colors.brandInactiveColor
|
||||
})('type');
|
||||
|
||||
|
||||
module.exports = styled(Label)`
|
||||
color: ${color};
|
||||
|
||||
${breakpoints.medium`
|
||||
float: right;
|
||||
text-align: right;
|
||||
`}
|
||||
`;
|
67
ui/src/components/form/outlet.js
Normal file
67
ui/src/components/form/outlet.js
Normal file
@ -0,0 +1,67 @@
|
||||
const constants = require('../../shared/constants');
|
||||
const fns = require('../../shared/functions');
|
||||
const Styled = require('styled-components');
|
||||
|
||||
const {
|
||||
colors,
|
||||
boxes
|
||||
} = constants;
|
||||
|
||||
const {
|
||||
remcalc
|
||||
} = fns;
|
||||
|
||||
const {
|
||||
css
|
||||
} = Styled;
|
||||
|
||||
const colorWithDisabled = (props) => props.disabled
|
||||
? colors.brandInactiveColor
|
||||
: colors.fonts.regular;
|
||||
|
||||
const colorWithDefaultValue = (props) => props.value === props.defaultValue
|
||||
? colors.brandInactiveColor
|
||||
: colorWithDisabled(props);
|
||||
|
||||
const color = (props) => props.defaultValue
|
||||
? colorWithDefaultValue(props)
|
||||
: colorWithDisabled(props);
|
||||
|
||||
const height = (props) => !props.multiple
|
||||
? remcalc(48)
|
||||
: 'auto';
|
||||
|
||||
const paddingTop = (props) => props.multiple
|
||||
? remcalc(20)
|
||||
: remcalc(13);
|
||||
|
||||
module.exports = css`
|
||||
box-sizing: border-box;
|
||||
|
||||
width: 100%;
|
||||
height: ${height};
|
||||
|
||||
margin-bottom: ${remcalc(8)};
|
||||
margin-top: ${remcalc(8)};
|
||||
padding: ${paddingTop} ${remcalc(18)};
|
||||
|
||||
border-radius: ${boxes.borderRadius};
|
||||
background-color: ${colors.brandPrimaryColor};
|
||||
box-shadow: ${boxes.insetShaddow};
|
||||
border: ${boxes.border.unchecked};
|
||||
|
||||
font-size: ${remcalc(16)};
|
||||
line-height: normal !important;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-stretch: normal;
|
||||
color: ${color};
|
||||
|
||||
appearance: none;
|
||||
outline: 0;
|
||||
|
||||
&:focus {
|
||||
border-color: ${colors.brandPrimary};
|
||||
outline: 0;
|
||||
}
|
||||
`;
|
7
ui/src/components/form/view.js
Normal file
7
ui/src/components/form/view.js
Normal file
@ -0,0 +1,7 @@
|
||||
const Styled = require('styled-components');
|
||||
|
||||
const {
|
||||
default: styled
|
||||
} = Styled;
|
||||
|
||||
module.exports = styled.div``;
|
@ -1,162 +1,80 @@
|
||||
const React = require('react');
|
||||
|
||||
const composers = require('../../shared/composers');
|
||||
const constants = require('../../shared/constants');
|
||||
const fns = require('../../shared/functions');
|
||||
const React = require('react');
|
||||
const Styled = require('styled-components');
|
||||
|
||||
const {
|
||||
boxes,
|
||||
colors
|
||||
} = constants;
|
||||
const Label = require('../form/label');
|
||||
const LabelRow = require('../form/label-row');
|
||||
const Msg = require('../form/msg');
|
||||
const Outlet = require('../form/outlet');
|
||||
const View = require('../form/view');
|
||||
|
||||
const {
|
||||
remcalc,
|
||||
rndId
|
||||
} = fns;
|
||||
|
||||
const {
|
||||
baseBox
|
||||
} = composers;
|
||||
|
||||
const {
|
||||
default: styled,
|
||||
css
|
||||
default: styled
|
||||
} = Styled;
|
||||
|
||||
const successBakcground = css`
|
||||
background-color: ${colors.brandSecondary};
|
||||
background-image: url('./input-confirm.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: 98% ${remcalc(20)};
|
||||
const StyledInput = styled.input`
|
||||
${Outlet}
|
||||
`;
|
||||
|
||||
const defaultBackground = css`
|
||||
background-color: ${colors.brandSecondary};
|
||||
`;
|
||||
const Input = (props) => {
|
||||
const {
|
||||
children,
|
||||
id = rndId(),
|
||||
label = '',
|
||||
error = '',
|
||||
warning = ''
|
||||
} = props;
|
||||
|
||||
const Label = styled.label`
|
||||
color: ${props => props.error ? colors.alert : colors.fonts.regular}
|
||||
`;
|
||||
const viewProps = [
|
||||
'children',
|
||||
'style',
|
||||
'className'
|
||||
];
|
||||
|
||||
const InputField = styled.input`
|
||||
${baseBox()};
|
||||
// reset props for <input />
|
||||
const newProps = Object.keys(props).reduce((sum, key) => ({
|
||||
...sum,
|
||||
[key]: viewProps.indexOf(key) < 0 ? props[key] : null
|
||||
}),{});
|
||||
|
||||
${props => props.success ? successBakcground : defaultBackground }
|
||||
const _label = !label.length ? null : (
|
||||
<Label htmlFor={id}>
|
||||
{label}
|
||||
</Label>
|
||||
);
|
||||
|
||||
border-color: ${props => props.error ? colors.alert : 'auto'}
|
||||
color: ${props => props.error ? colors.alert : colors.fonts.semibold}
|
||||
display: block;
|
||||
font-size: ${remcalc(16)};
|
||||
padding: ${remcalc('15 18')};
|
||||
visibility: visible;
|
||||
width: 100%;
|
||||
const msgType = error ? 'error' : (warning ? 'warning' : null);
|
||||
|
||||
&:focus {
|
||||
border-color: ${boxes.border.checked};
|
||||
outline: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const Error = styled.span`
|
||||
float: right;
|
||||
color: ${colors.alert};
|
||||
font-size: ${remcalc(14)};
|
||||
`;
|
||||
|
||||
const Input = ({
|
||||
autoComplete,
|
||||
autoFocus,
|
||||
children,
|
||||
className,
|
||||
disabled = false,
|
||||
error,
|
||||
form,
|
||||
id,
|
||||
inputMode,
|
||||
label,
|
||||
labelledby,
|
||||
list,
|
||||
name,
|
||||
onChange,
|
||||
pattern,
|
||||
placeholder,
|
||||
readOnly,
|
||||
required,
|
||||
selectionDirection,
|
||||
spellCheck,
|
||||
style,
|
||||
success,
|
||||
tabIndex,
|
||||
type,
|
||||
value
|
||||
}) => {
|
||||
const _label = label || children;
|
||||
const _children = label && children ? children : null;
|
||||
const _error = error ? (<Error>{error}</Error>) : null;
|
||||
const _msg = !(error || warning) ? null : (
|
||||
<Msg type={msgType}>
|
||||
{error ? error : warning}
|
||||
</Msg>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Label
|
||||
error={error}
|
||||
htmlFor={id}
|
||||
>
|
||||
<View className={props.className} style={props.style}>
|
||||
<LabelRow>
|
||||
{_label}
|
||||
</Label>
|
||||
{_error}
|
||||
<InputField
|
||||
aria-labelledby={labelledby}
|
||||
autoComplete={autoComplete}
|
||||
autoFocus={autoFocus}
|
||||
disabled={disabled}
|
||||
error={error}
|
||||
form={form}
|
||||
id={id}
|
||||
inputMode={inputMode}
|
||||
list={list}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
pattern={pattern}
|
||||
placeholder={placeholder}
|
||||
readOnly={readOnly}
|
||||
required={required}
|
||||
selectionDirection={selectionDirection}
|
||||
spellCheck={spellCheck}
|
||||
success={success}
|
||||
tabIndex={tabIndex}
|
||||
type={type}
|
||||
value={value}
|
||||
/>
|
||||
{_children}
|
||||
</div>
|
||||
{_msg}
|
||||
</LabelRow>
|
||||
<StyledInput {...newProps} />
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
Input.propTypes = {
|
||||
autoComplete: React.PropTypes.string,
|
||||
autoFocus: React.PropTypes.bool,
|
||||
children: React.PropTypes.node,
|
||||
className: React.PropTypes.string,
|
||||
disabled: React.PropTypes.bool,
|
||||
error: React.PropTypes.string,
|
||||
form: React.PropTypes.string,
|
||||
id: React.PropTypes.string,
|
||||
inputMode: React.PropTypes.string,
|
||||
label: React.PropTypes.string,
|
||||
labelledby: React.PropTypes.string,
|
||||
list: React.PropTypes.string,
|
||||
name: React.PropTypes.string,
|
||||
onChange: React.PropTypes.func,
|
||||
pattern: React.PropTypes.string,
|
||||
placeholder: React.PropTypes.string,
|
||||
readOnly: React.PropTypes.bool,
|
||||
required: React.PropTypes.bool,
|
||||
selectionDirection: React.PropTypes.string,
|
||||
spellCheck: React.PropTypes.bool,
|
||||
style: React.PropTypes.object,
|
||||
success: React.PropTypes.bool,
|
||||
tabIndex: React.PropTypes.string,
|
||||
type: React.PropTypes.string,
|
||||
value: React.PropTypes.string
|
||||
warning: React.PropTypes.string
|
||||
};
|
||||
|
||||
module.exports = Input;
|
||||
|
@ -1,123 +1,91 @@
|
||||
const fns = require('../../shared/functions');
|
||||
const composers = require('../../shared/composers');
|
||||
const React = require('react');
|
||||
const Styled = require('styled-components');
|
||||
|
||||
const {
|
||||
rndId,
|
||||
remcalc
|
||||
} = fns;
|
||||
const Label = require('../form/label');
|
||||
const LabelRow = require('../form/label-row');
|
||||
const Msg = require('../form/msg');
|
||||
const Outlet = require('../form/outlet');
|
||||
const View = require('../form/view');
|
||||
|
||||
const {
|
||||
pseudoEl
|
||||
} = composers;
|
||||
rndId
|
||||
} = fns;
|
||||
|
||||
const {
|
||||
default: styled
|
||||
} = Styled;
|
||||
|
||||
// TODO: this should be a constant
|
||||
const StyledLabel = styled.div`
|
||||
color: #464646;
|
||||
`;
|
||||
|
||||
const SelectWrapper = styled.div`
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
&:after {
|
||||
border-left: ${remcalc(5)} solid transparent;
|
||||
border-right: ${remcalc(5)} solid transparent;
|
||||
border-bottom: ${remcalc(5)} solid black;
|
||||
|
||||
${pseudoEl({
|
||||
top: remcalc(25),
|
||||
right: remcalc(20)
|
||||
})}
|
||||
}
|
||||
`;
|
||||
|
||||
const defaultValue = rndId();
|
||||
|
||||
const StyledSelect = styled.select`
|
||||
font-size: ${remcalc(16)};
|
||||
min-width: ${remcalc(288)};
|
||||
min-height: ${remcalc(54)};
|
||||
border-radius: ${remcalc(4)};
|
||||
padding-left: ${remcalc(20)};
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: inset 0 ${remcalc(3)} 0 0 rgba(0, 0, 0, 0.05);
|
||||
border: solid ${remcalc(1)} #D8D8D8;
|
||||
-webkit-appearance: none;
|
||||
|
||||
&:before {
|
||||
${pseudoEl()}
|
||||
}
|
||||
|
||||
/* select[multiple] is valid CSS syntax - not added to lint library yet */
|
||||
/* stylelint-disable */
|
||||
&[multiple] {
|
||||
/* stylelint-enable */
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
|
||||
& option {
|
||||
padding-left: ${remcalc(15)};
|
||||
padding-right: ${remcalc(15)};
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
${Outlet}
|
||||
`;
|
||||
|
||||
const Select = ({
|
||||
autoFocus,
|
||||
children,
|
||||
className,
|
||||
disabled,
|
||||
form,
|
||||
id = rndId(),
|
||||
label,
|
||||
multiple,
|
||||
name,
|
||||
required,
|
||||
selected
|
||||
}) => {
|
||||
return (
|
||||
<div className={className}>
|
||||
<StyledLabel htmlFor={id}>
|
||||
{label}
|
||||
</StyledLabel>
|
||||
const Select = (props) => {
|
||||
const {
|
||||
children,
|
||||
disabled = false,
|
||||
error = '',
|
||||
id = rndId(),
|
||||
label = '',
|
||||
multiple = false,
|
||||
placeholder = '',
|
||||
value = defaultValue,
|
||||
warning = ''
|
||||
} = props;
|
||||
|
||||
<SelectWrapper>
|
||||
<StyledSelect
|
||||
autoFocus={autoFocus}
|
||||
disabled={disabled}
|
||||
form={form}
|
||||
id={id}
|
||||
label={label}
|
||||
multiple={multiple}
|
||||
name={name}
|
||||
required={required}
|
||||
selected={selected}
|
||||
>
|
||||
{children}
|
||||
</StyledSelect>
|
||||
</SelectWrapper>
|
||||
</div>
|
||||
const _label = !label.length ? null : (
|
||||
<Label htmlFor={id}>
|
||||
{label}
|
||||
</Label>
|
||||
);
|
||||
|
||||
const _placeholder = !placeholder ? null : (
|
||||
<option disabled value={defaultValue}>
|
||||
{placeholder}
|
||||
</option>
|
||||
);
|
||||
|
||||
const msgType = error ? 'error' : (warning ? 'warning' : null);
|
||||
|
||||
const _msg = !(error || warning) ? null : (
|
||||
<Msg type={msgType}>
|
||||
{error ? error : warning}
|
||||
</Msg>
|
||||
);
|
||||
|
||||
return (
|
||||
<View {...props} id=''>
|
||||
<LabelRow>
|
||||
{_label}
|
||||
{_msg}
|
||||
</LabelRow>
|
||||
<StyledSelect
|
||||
defaultValue={defaultValue}
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
multiple={multiple}
|
||||
placeholder={placeholder}
|
||||
value={_placeholder ? value : undefined}
|
||||
>
|
||||
{_placeholder}
|
||||
{children}
|
||||
</StyledSelect>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
Select.propTypes = {
|
||||
autoFocus: React.PropTypes.bool,
|
||||
children: React.PropTypes.node,
|
||||
className: React.PropTypes.string,
|
||||
disabled: React.PropTypes.bool,
|
||||
form: React.PropTypes.string,
|
||||
error: React.PropTypes.string,
|
||||
id: React.PropTypes.string,
|
||||
label: React.PropTypes.string,
|
||||
multiple: React.PropTypes.bool,
|
||||
name: React.PropTypes.string,
|
||||
required: React.PropTypes.bool,
|
||||
selected: React.PropTypes.bool
|
||||
placeholder: React.PropTypes.string,
|
||||
value: React.PropTypes.string,
|
||||
warning: React.PropTypes.string
|
||||
};
|
||||
|
||||
module.exports = Select;
|
||||
|
@ -5,22 +5,76 @@ const {
|
||||
} = require('@kadira/storybook');
|
||||
|
||||
const Select = require('./');
|
||||
const Base = require('../base');
|
||||
|
||||
|
||||
storiesOf('Select', module)
|
||||
.add('Default', () => (
|
||||
<Select label='example select'>
|
||||
<option>Apple</option>
|
||||
<option>Banana</option>
|
||||
<option>Pear</option>
|
||||
<option>Orange</option>
|
||||
</Select>
|
||||
<Base>
|
||||
<Select label='Data Centers' placeholder='Select Location'>
|
||||
<option value="1">Amsterdam, EU</option>
|
||||
<option>San Francisco, USA</option>
|
||||
<option>Seoul, South Korea</option>
|
||||
<option>Tokyo, Japan</option>
|
||||
</Select>
|
||||
</Base>
|
||||
))
|
||||
.add('disabled', () => (
|
||||
<Base>
|
||||
<Select
|
||||
disabled
|
||||
label='Data Centers'
|
||||
placeholder='Select Location'
|
||||
value='2'
|
||||
>
|
||||
<option value="1">Amsterdam, EU</option>
|
||||
<option>San Francisco, USA</option>
|
||||
<option>Seoul, South Korea</option>
|
||||
<option>Tokyo, Japan</option>
|
||||
</Select>
|
||||
</Base>
|
||||
))
|
||||
.add('selected', () => (
|
||||
<Base>
|
||||
<Select
|
||||
label='Data Centers'
|
||||
placeholder='Select Location'
|
||||
value='2'
|
||||
>
|
||||
<option value="1">Amsterdam, EU</option>
|
||||
<option>San Francisco, USA</option>
|
||||
<option>Seoul, South Korea</option>
|
||||
<option>Tokyo, Japan</option>
|
||||
</Select>
|
||||
</Base>
|
||||
))
|
||||
.add('multiple', () => (
|
||||
<Select label='example multiple select' multiple>
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
</Select>
|
||||
));
|
||||
<Base>
|
||||
<Select label='Data Centers' multiple>
|
||||
<option>Amsterdam, EU</option>
|
||||
<option>San Francisco, USA</option>
|
||||
<option>Seoul, South Korea</option>
|
||||
<option>Tokyo, Japan</option>
|
||||
</Select>
|
||||
</Base>
|
||||
))
|
||||
.add('warning', () => (
|
||||
<Base>
|
||||
<Select label='Data Centers' warning='Be warned!'>
|
||||
<option>Amsterdam, EU</option>
|
||||
<option>San Francisco, USA</option>
|
||||
<option>Seoul, South Korea</option>
|
||||
<option>Tokyo, Japan</option>
|
||||
</Select>
|
||||
</Base>
|
||||
))
|
||||
.add('error', () => (
|
||||
<Base>
|
||||
<Select error='How dare you?!' label='Data Centers'>
|
||||
<option>Amsterdam, EU</option>
|
||||
<option>San Francisco, USA</option>
|
||||
<option>Seoul, South Korea</option>
|
||||
<option>Tokyo, Japan</option>
|
||||
</Select>
|
||||
</Base>
|
||||
));
|
||||
|
Loading…
Reference in New Issue
Block a user