Create FieldCheckboxGroup: using FieldGroupCheckbox as starting point

This commit is contained in:
Vesa Luusua 2018-04-16 12:06:39 +03:00
parent 0e86cd5322
commit 7a81956dd1
5 changed files with 278 additions and 0 deletions

View file

@ -0,0 +1,28 @@
@import '../../marketplace.css';
.root {
margin: 0;
padding: 0;
border: none;
}
.list {
margin: 0;
}
.twoColumns {
@media (--viewportMedium) {
columns: 2;
}
}
.item {
padding: 2px 0;
/* Fix broken multi-column layout in Chrome */
page-break-inside: avoid;
@media (--viewportMedium) {
padding: 4px 0;
}
}

View file

@ -0,0 +1,151 @@
import React from 'react';
import { Form as FinalForm, FormSpy } from 'react-final-form';
import arrayMutators from 'final-form-arrays';
import { Button } from '../../components';
import FieldCheckboxGroup from './FieldCheckboxGroup';
import { requiredFieldArrayCheckbox } from '../../util/validators';
const formName = 'Styleguide.FieldCheckboxGroup';
const formNameRequired = 'Styleguide.FieldCheckboxGroupRequired';
const label = <h3>Amenities</h3>;
const commonProps = {
label: label,
options: [
{
key: 'towels',
label: 'Towels',
},
{
key: 'bathroom',
label: 'Bathroom',
},
{
key: 'swimming_pool',
label: 'Swimming pool',
},
{
key: 'own_drinks',
label: 'Own drinks allowed',
},
{
key: 'jacuzzi',
label: 'Jacuzzi',
},
{
key: 'audiovisual_entertainment',
label: 'Audiovisual entertainment',
},
{
key: 'barbeque',
label: 'Barbeque',
},
{
key: 'own_food_allowed',
label: 'Own food allowed',
},
],
twoColumns: true,
};
const optionalProps = {
name: 'amenities-optional',
id: 'amenities-optional',
...commonProps,
};
const requiredProps = {
name: 'amenities-required',
id: `${formNameRequired}.amenities-required`,
...commonProps,
validate: requiredFieldArrayCheckbox('this is required'),
};
const tosProps = {
name: 'terms-of-service',
id: `${formNameRequired}.tos-accepted`,
options: [
{
key: 'tos',
label: 'Terms of Service',
},
],
validate: requiredFieldArrayCheckbox('You need to accept Terms of Service'),
};
const formComponent = country => props => (
<FinalForm
{...props}
mutators={{ ...arrayMutators }}
render={fieldRenderProps => {
const { handleSubmit, invalid, submitting, componentProps, onChange } = fieldRenderProps;
const submitDisabled = invalid || submitting;
return (
<form
onSubmit={e => {
e.preventDefault();
handleSubmit(e);
}}
>
<FormSpy onChange={onChange} />
<FieldCheckboxGroup {...componentProps} />
<Button style={{ marginTop: 24 }} type="submit" disabled={submitDisabled}>
Submit
</Button>
</form>
);
}}
/>
);
export const Optional = {
component: formComponent(formName),
props: {
onChange: formState => {
if (formState.dirty) {
console.log('form values changed to:', formState.values);
}
},
onSubmit: values => {
console.log('Submit values: ', values);
},
initialValues: { [optionalProps.name]: ['jacuzzi', 'towels'] },
componentProps: optionalProps,
},
group: 'inputs',
};
export const Required = {
component: formComponent(formNameRequired),
props: {
onChange: formState => {
if (formState.dirty) {
console.log('form values changed to:', formState.values);
}
},
onSubmit: values => {
console.log('Submit values: ', values);
},
componentProps: requiredProps,
},
group: 'inputs',
};
export const ToSAccepted = {
component: formComponent(formNameRequired),
props: {
onChange: formState => {
if (formState.dirty) {
console.log('form values changed to:', formState.values);
}
},
onSubmit: values => {
console.log('Submit values: ', values);
},
componentProps: tosProps,
},
group: 'inputs',
};

View file

@ -0,0 +1,96 @@
/**
* NOTE this component is part of react-final-form instead of Redux Form.
*/
/*
* Renders a group of checkboxes that can be used to select
* multiple values from a set of options.
*
* The corresponding component when rendering the selected
* values is PropertyGroup.
*
*/
import React, { Component } from 'react';
import { arrayOf, bool, node, shape, string } from 'prop-types';
import classNames from 'classnames';
import { FieldArray } from 'react-final-form-arrays';
import { FieldCheckbox, ValidationError } from '../../components';
import css from './FieldCheckboxGroup.css';
class FieldCheckboxRenderer extends Component {
constructor(props) {
super(props);
this.state = { touched: false };
}
componentWillReceiveProps(nextProps) {
// FieldArray doesn't have touched prop, so we'll keep track of it
if (!this.state.touched) {
this.setState({ touched: nextProps.meta.dirty });
}
}
render() {
const { className, rootClassName, label, twoColumns, id, fields, options, meta } = this.props;
const touched = this.state.touched;
const classes = classNames(rootClassName || css.root, className);
const listClasses = twoColumns ? classNames(css.list, css.twoColumns) : css.list;
return (
<fieldset className={classes}>
{label ? <legend>{label}</legend> : null}
<ul className={listClasses}>
{options.map((option, index) => {
const fieldId = `${id}.${option.key}`;
return (
<li key={fieldId} className={css.item}>
<FieldCheckbox
id={fieldId}
name={fields.name}
label={option.label}
value={option.key}
/>
</li>
);
})}
</ul>
<ValidationError fieldMeta={{ ...meta, touched }} />
</fieldset>
);
}
}
FieldCheckboxRenderer.defaultProps = {
rootClassName: null,
className: null,
label: null,
twoColumns: false,
};
FieldCheckboxRenderer.propTypes = {
rootClassName: string,
className: string,
id: string.isRequired,
label: node,
options: arrayOf(
shape({
key: string.isRequired,
label: node.isRequired,
})
).isRequired,
twoColumns: bool,
};
const FieldCheckboxGroup = props => <FieldArray component={FieldCheckboxRenderer} {...props} />;
// Name and component are required fields for FieldArray.
// Component-prop we define in this file, name needs to be passed in
FieldCheckboxGroup.propTypes = {
name: string.isRequired,
};
export default FieldCheckboxGroup;

View file

@ -32,6 +32,7 @@ export { default as ExternalLink } from './ExternalLink/ExternalLink';
export { default as FieldBirthdayInput } from './FieldBirthdayInput/FieldBirthdayInput';
export { default as FieldBoolean } from './FieldBoolean/FieldBoolean';
export { default as FieldCheckbox } from './FieldCheckbox/FieldCheckbox';
export { default as FieldCheckboxGroup } from './FieldCheckboxGroup/FieldCheckboxGroup';
export { default as FieldCurrencyInput } from './FieldCurrencyInput/FieldCurrencyInput';
export { default as FieldDateInput } from './FieldDateInput/FieldDateInput';
export { default as FieldDateRangeInput } from './FieldDateRangeInput/FieldDateRangeInput';

View file

@ -8,6 +8,7 @@ import * as ExpandingTextarea from './components/ExpandingTextarea/ExpandingText
import * as FieldBirthdayInput from './components/FieldBirthdayInput/FieldBirthdayInput.example';
import * as FieldBoolean from './components/FieldBoolean/FieldBoolean.example';
import * as FieldCheckbox from './components/FieldCheckbox/FieldCheckbox.example';
import * as FieldCheckboxGroup from './components/FieldCheckboxGroup/FieldCheckboxGroup.example';
import * as FieldCurrencyInput from './components/FieldCurrencyInput/FieldCurrencyInput.example';
import * as FieldDateInput from './components/FieldDateInput/FieldDateInput.example';
import * as FieldDateRangeInput from './components/FieldDateRangeInput/FieldDateRangeInput.example';
@ -100,6 +101,7 @@ export {
FieldBirthdayInput,
FieldBoolean,
FieldCheckbox,
FieldCheckboxGroup,
FieldCurrencyInput,
FieldDateInput,
FieldDateRangeInput,