Add SelectField component

This commit is contained in:
Kimmo Puputti 2017-06-21 10:26:43 +03:00
parent d1e7483842
commit a25d535fd6
6 changed files with 170 additions and 1 deletions

View file

@ -0,0 +1,17 @@
@import '../../marketplace.css';
.root {
}
.select {
border-bottom-color: var(--attentionColor);
}
.selectSuccess {
border-bottom-color: var(--successColor);
}
.selectError {
border-bottom-color: var(--failColor);
}

View file

@ -0,0 +1,45 @@
/* eslint-disable no-console */
import React from 'react';
import { reduxForm, propTypes as formPropTypes } from 'redux-form';
import * as validators from '../../util/validators';
import { Button } from '../../components';
import SelectField from './SelectField';
const formName = 'Styleguide.SelectField.Form';
const FormComponent = props => {
const { form, handleSubmit, invalid, pristine, submitting } = props;
const required = validators.required('This field is required');
const submitDisabled = invalid || pristine || submitting;
return (
<form onSubmit={handleSubmit}>
<SelectField
id={`${form}.select1`}
name="select1"
label="Choose an option:"
validate={required}
>
<option value="">Pick something...</option>
<option value="first">First option</option>
<option value="second">Second option</option>
</SelectField>
<Button style={{ marginTop: 24 }} type="submit" disabled={submitDisabled}>Submit</Button>
</form>
);
};
FormComponent.propTypes = formPropTypes;
const Form = reduxForm({
form: formName,
})(FormComponent);
export const Select = {
component: Form,
props: {
onSubmit: values => {
console.log('submit values:', values);
},
},
group: 'inputs',
};

View file

@ -0,0 +1,67 @@
import React, { PropTypes } from 'react';
import { Field } from 'redux-form';
import classNames from 'classnames';
import { ValidationError } from '../../components';
import css from './SelectField.css';
const SelectFieldComponent = props => {
const { rootClassName, className, id, label, input, meta, children } = props;
if (label && !id) {
throw new Error('id required when a label is given');
}
const { valid, invalid, touched, error } = meta;
// Error message and input error styles are only shown if the
// field has been touched and the validation has failed.
const hasError = touched && invalid && error;
const selectClasses = classNames(css.select, {
[css.selectSuccess]: valid,
[css.selectError]: hasError,
});
const selectProps = { className: selectClasses, ...input };
const classes = classNames(rootClassName || css.root, className);
return (
<div className={classes}>
{label ? <label htmlFor={id}>{label}</label> : null}
<select {...selectProps}>{children}</select>
<ValidationError fieldMeta={meta} />
</div>
);
};
SelectFieldComponent.defaultProps = {
rootClassName: null,
className: null,
id: null,
label: null,
children: null,
};
const { string, object, node } = PropTypes;
SelectFieldComponent.propTypes = {
rootClassName: string,
className: string,
// Label is optional, but if it is given, an id is also required so
// the label can reference the input in the `for` attribute
id: string,
label: string,
// Generated by redux-form's Field component
input: object.isRequired,
meta: object.isRequired,
children: node,
};
const SelectField = props => {
return <Field component={SelectFieldComponent} {...props} />;
};
export default SelectField;

View file

@ -40,6 +40,7 @@ import SaleDetailsPanel from './SaleDetailsPanel/SaleDetailsPanel';
import SearchIcon from './SearchIcon/SearchIcon';
import SearchResultsPanel from './SearchResultsPanel/SearchResultsPanel';
import Select from './Select/Select';
import SelectField from './SelectField/SelectField';
import StripeBankAccountToken from './StripeBankAccountToken/StripeBankAccountToken';
import TabNav from './TabNav/TabNav';
import Tabs from './Tabs/Tabs';
@ -92,6 +93,7 @@ export {
SearchIcon,
SearchResultsPanel,
Select,
SelectField,
StripeBankAccountToken,
TabNav,
Tabs,

View file

@ -16,6 +16,7 @@ import * as LocationAutocompleteInput
from './components/LocationAutocompleteInput/LocationAutocompleteInput.example';
import * as PaginationLinks from './components/PaginationLinks/PaginationLinks.example';
import * as ResponsiveImage from './components/ResponsiveImage/ResponsiveImage.example';
import * as SelectField from './components/SelectField/SelectField.example';
import * as StripeBankAccountToken
from './components/StripeBankAccountToken/StripeBankAccountToken.example';
import * as TabNav from './components/TabNav/TabNav.example';
@ -73,6 +74,7 @@ export {
PasswordForgottenForm,
PayoutDetailsForm,
ResponsiveImage,
SelectField,
SignupForm,
StripeBankAccountToken,
StripePaymentForm,

View file

@ -373,6 +373,41 @@
outline: none;
}
}
--marketplaceSelectStyles: {
/* Dimensions */
display: block;
width: 100%;
margin: 0;
padding: 5px 0 4px 0;
/* Unset user agent styles */
appearance: none;
/* Borders */
border: none;
border-bottom-width: 2px;
border-bottom-style: solid;
border-bottom-color: var(--marketplaceColor);
border-radius: 0;
/* Background */
background-image: url('data:image/svg+xml;utf8,<svg width="16" height="10" viewBox="0 0 16 10" xmlns="http://www.w3.org/2000/svg"><path d="M15.027 2.5a.577.577 0 0 0 0-.813L13.545.214a.566.566 0 0 0-.804 0L8 4.955 3.259.215a.566.566 0 0 0-.804 0L.973 1.686a.577.577 0 0 0 0 .813l6.625 6.616c.223.223.58.223.804 0L15.027 2.5z" fill="#4A4A4A" fill-rule="evenodd"/></svg>');
background-size: 16px 16px;
background-position: center right;
/* Effects */
cursor: pointer;
transition: border-bottom-color var(--transitionStyle);
&:hover,
&:focus {
border-bottom-color: var(--matterColor);
outline: none;
}
}
}
/* ================ Custom media queries ================ */
@ -401,6 +436,7 @@ h6 { @apply --marketplaceH6FontStyles; }
html,
input,
textarea,
select,
li {
@apply --marketplaceDefaultFontStyles
}
@ -441,7 +477,7 @@ label {
}
select {
border: none;
@apply --marketplaceSelectStyles;
}
input {