Skip to content

Commit

Permalink
Merge pull request #1 from deskpro/feature/form-schema
Browse files Browse the repository at this point in the history
Form Layouts
  • Loading branch information
jducro committed May 24, 2019
1 parent eb875c0 commit f8942d6
Show file tree
Hide file tree
Showing 14 changed files with 983 additions and 93 deletions.
8 changes: 5 additions & 3 deletions packages/portal-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"classnames": "^2.2.5",
"formik": "^0.11.11",
"immutable": "^3.8.2",
"lodash": "^4.17.10",
"moment": "^2.22.1",
"moment-hijri": "^2.0.1",
"raw-loader": "^0.5.1",
Expand Down Expand Up @@ -42,6 +43,7 @@
"@storybook/addon-links": "^3.4.1",
"@storybook/addons": "^3.4.1",
"@storybook/react": "^3.4.1",
"ajv": "^6.5.2",
"autoprefixer": "7.1.6",
"babel-core": "6.26.0",
"babel-eslint": "7.2.3",
Expand Down Expand Up @@ -95,8 +97,8 @@
"whatwg-fetch": "2.0.3"
},
"peerDependencies": {
"react": "^15.6.2 || ^16.0.0-alpha",
"prop-types": "^15.6.1"
"prop-types": "^15.6.1",
"react": "^15.6.2 || ^16.0.0-alpha"
},
"jest": {
"collectCoverageFrom": [
Expand Down Expand Up @@ -137,4 +139,4 @@
"eslint"
]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class Checkboxes extends Field {
name={name}
type="checkbox"
value={option.value}
checked={form.values[name].includes(option.value)}
checked={!!form.values[name] && form.values[name].includes(option.value)}
onChange={(e) => {
if (e.target.checked) arrayHelpers.push(option.value);
else {
Expand Down
33 changes: 12 additions & 21 deletions packages/portal-components/src/Components/Field.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Field extends React.Component {
description: '',
className: '',
id: null,
children: null,
children: null
};

constructor(props) {
Expand All @@ -34,10 +34,7 @@ class Field extends React.Component {

renderField = () => {
const {
name,
children,
className,
...props
name, children, className, ...props
} = this.props;

return (
Expand All @@ -54,22 +51,16 @@ class Field extends React.Component {
};

renderDescription = () => {
const {
description
} = this.props;
const { description } = this.props;

if (description) {
return (
<span className="dp-pc_description">{description}</span>
);
return <span className="dp-pc_description">{description}</span>;
}
return null;
};

renderLabel = () => {
const {
label,
} = this.props;
const { label } = this.props;
const htmlFor = this.id;
/* eslint-disable jsx-a11y/label-has-for */
return (
Expand All @@ -85,9 +76,7 @@ class Field extends React.Component {
renderDivider = () => <div className="divider" />;

render() {
const {
name,
} = this.props;
const { name } = this.props;
return (
<FormikField
name={name}
Expand All @@ -96,12 +85,14 @@ class Field extends React.Component {
const touch = getIn(form.touched, name);
const value = getIn(form.values, name);
return (
<div className={classNames('dp-pc_field', {
'dp-pc_error': touch && error, 'dp-pc_empty': value === null || value.length === 0
})}
<div
className={classNames('dp-pc_field', {
'dp-pc_error': touch && error,
'dp-pc_empty': !value || value === null || value.length === 0
})}
>
{this.renderField(form)}
{ touch && error ? <ErrorMessage name={name} form={form} /> : this.renderDescription() }
{touch && error ? <ErrorMessage name={name} form={form} /> : this.renderDescription()}
{this.renderIndicator()}
{this.renderLabel()}
{this.renderDivider()}
Expand Down
86 changes: 86 additions & 0 deletions packages/portal-components/src/Components/FieldLayout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { Fragment, PureComponent } from 'react';
import PropTypes from 'prop-types';

import FileUpload from './Inputs/FileUpload';
import Text from './Inputs/Text';
import Textarea from './Inputs/Textarea';
import DatePicker from './Inputs/DatePicker';
import DateTimePicker from './Inputs/DateTimePicker';
import Checkboxes from './Choices/Checkboxes';
import DropDown from './Choices/DropDown';
import MultipleDropDown from './Choices/MultipleDropDown';
import Radio from './Choices/Radio';
import { LayoutConfig } from '../layouts/Layout';

const components = {
file: FileUpload,
text: Text,
textarea: Textarea,
date: DatePicker,
datetime: DateTimePicker,
checkbox: Checkboxes,
choice: DropDown,
radio: Radio,
multichoice: MultipleDropDown
};

class FieldLayout extends PureComponent {
static propTypes = {
layouts: PropTypes.instanceOf(LayoutConfig).isRequired,
values: PropTypes.object.isRequired,
fileUploadUrl: PropTypes.string,
csrfToken: PropTypes.string,
resetForm: PropTypes.func.isRequired,
};

static defaultProps = {
fileUploadUrl: '',
csrfToken: ''
};

static getDerivedStateFromProps({ layouts, values }, { activeLayout }) {
const newLayout = layouts.getMatchingLayout(values);
if (newLayout !== activeLayout) {
return { activeLayout: newLayout };
}
return null;
}

state = { activeLayout: null };

componentDidUpdate(_, { activeLayout }) {
// Reset form with new defaults when the layout is changed.
if (activeLayout !== this.state.activeLayout) {
const defaults = this.state.activeLayout.getDefaultValues();
this.props.resetForm({ ...defaults, ...this.props.values });
}
}

render() {
const { activeLayout } = this.state;
return (
<Fragment>
{activeLayout.fields.map((field) => {
const Component = components[field.type];
if (!Component) {
// Do we need to warn developer that component is not found?
return null;
}
const props = { values: this.props.values };
// TODO: it looks like a hardcode. Can we move this into schema definition?
if (field.type === 'file') {
props.file = this.props.fileUploadUrl;
props.csrfToken = this.props.csrfToken;
}
return (
<span key={field.name}>
<Component {...field} {...props} />
</span>
);
})}
</Fragment>
);
}
}

export default FieldLayout;
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import DropDown from './Choices/DropDown';

class TicketDepartment extends React.Component {
static propTypes = {
departments: PropTypes.object.isRequired,
handleChange: PropTypes.func.isRequired,
departments: PropTypes.array.isRequired,
handleChange: PropTypes.func.isRequired
};

render() {
Expand All @@ -14,7 +14,7 @@ class TicketDepartment extends React.Component {
<DropDown
name="department"
label="Department"
options={departments.toArray().map(d => ({ label: d.get('title'), value: d.get('id') }))}
options={departments}
handleChange={handleChange}
clearable={false}
/>
Expand Down
Loading

0 comments on commit f8942d6

Please sign in to comment.