Add formComponents prop to forms to enable customizing a specific form's components

This commit is contained in:
SachaG 2018-09-23 08:13:09 +09:00
parent bfd336871e
commit 07427a2c96
8 changed files with 61 additions and 33 deletions

View file

@ -230,6 +230,15 @@ class SmartForm extends Component {
return data;
};
/*
Get form components, in case any has been overwritten for this specific form
*/
getFormComponents = () => {
return { ...Components, ...this.props.formComponents }
}
// --------------------------------------------------------------------- //
// -------------------------------- Fields ----------------------------- //
// --------------------------------------------------------------------- //
@ -859,14 +868,15 @@ class SmartForm extends Component {
render() {
const fieldGroups = this.getFieldGroups();
const collectionName = this.getCollection()._name;
const FormComponents = this.getFormComponents();
return (
<div className={'document-' + this.getFormType()}>
<Formsy.Form onSubmit={this.submitForm} onKeyDown={this.formKeyDown} ref={e => { this.form = e; }}>
<Components.FormErrors errors={this.state.errors} />
<FormComponents.FormErrors errors={this.state.errors} />
{fieldGroups.map(group => (
<Components.FormGroup
<FormComponents.FormGroup
key={group.name}
{...group}
errors={this.state.errors}
@ -879,12 +889,13 @@ class SmartForm extends Component {
formType={this.getFormType()}
currentUser={this.props.currentUser}
disabled={this.state.disabled}
formComponents={FormComponents}
/>
))}
{this.props.repeatErrors && this.renderErrors()}
<Components.FormSubmit
<FormComponents.FormSubmit
submitLabel={this.props.submitLabel}
cancelLabel={this.props.cancelLabel}
revertLabel={this.props.revertLabel}
@ -935,6 +946,7 @@ SmartForm.propTypes = {
revertLabel: PropTypes.string,
repeatErrors: PropTypes.bool,
warnUnsavedChanges: PropTypes.bool,
formComponents: PropTypes.object,
// callbacks
submitCallback: PropTypes.func,

View file

@ -198,6 +198,7 @@ class FormComponent extends Component {
*/
getFormInput = () => {
const inputType = this.getType();
const FormComponents = this.props.formComponents;
// if input is a React component, use it
if (typeof this.props.input === 'function') {
@ -208,53 +209,53 @@ class FormComponent extends Component {
switch (inputType) {
case 'text':
return Components.FormComponentDefault;
return FormComponents.FormComponentDefault;
case 'number':
return Components.FormComponentNumber;
return FormComponents.FormComponentNumber;
case 'url':
return Components.FormComponentUrl;
return FormComponents.FormComponentUrl;
case 'email':
return Components.FormComponentEmail;
return FormComponents.FormComponentEmail;
case 'textarea':
return Components.FormComponentTextarea;
return FormComponents.FormComponentTextarea;
case 'checkbox':
return Components.FormComponentCheckbox;
return FormComponents.FormComponentCheckbox;
case 'checkboxgroup':
return Components.FormComponentCheckboxGroup;
return FormComponents.FormComponentCheckboxGroup;
case 'radiogroup':
return Components.FormComponentRadioGroup;
return FormComponents.FormComponentRadioGroup;
case 'select':
return Components.FormComponentSelect;
return FormComponents.FormComponentSelect;
case 'selectmultiple':
return Components.FormComponentSelectMultiple;
return FormComponents.FormComponentSelectMultiple;
case 'datetime':
return Components.FormComponentDateTime;
return FormComponents.FormComponentDateTime;
case 'date':
return Components.FormComponentDate;
return FormComponents.FormComponentDate;
case 'date2':
return Components.FormComponentDate2;
return FormComponents.FormComponentDate2;
case 'time':
return Components.FormComponentTime;
return FormComponents.FormComponentTime;
case 'statictext':
return Components.FormComponentStaticText;
return FormComponents.FormComponentStaticText;
default:
const CustomComponent = Components[this.props.input];
return CustomComponent ? CustomComponent : Components.FormComponentDefault;
const CustomComponent = FormComponents[this.props.input];
return CustomComponent ? CustomComponent : FormComponents.FormComponentDefault;
}
}
};
@ -269,17 +270,20 @@ class FormComponent extends Component {
return this.getFieldType() instanceof SimpleSchema
}
render() {
const FormComponents = this.props.formComponents;
if (this.props.intlInput) {
return <Components.FormIntl {...this.props} />;
return <FormComponents.FormIntl {...this.props} />;
} else if (this.props.nestedInput) {
if (this.isArrayField()) {
return <Components.FormNestedArray {...this.props} errors={this.getErrors()} value={this.getValue()}/>;
return <FormComponents.FormNestedArray {...this.props} errors={this.getErrors()} value={this.getValue()}/>;
} else if (this.isObjectField()) {
return <Components.FormNestedObject {...this.props} errors={this.getErrors()} value={this.getValue()}/>;
return <FormComponents.FormNestedObject {...this.props} errors={this.getErrors()} value={this.getValue()}/>;
}
}
return (
<Components.FormComponentInner
<FormComponents.FormComponentInner
{...this.props}
{...this.state}
inputType={this.getType()}
@ -290,6 +294,7 @@ class FormComponent extends Component {
onChange={this.handleChange}
clearField={this.clearField}
formInput={this.getFormInput()}
formComponents={FormComponents}
/>
);
}

View file

@ -38,12 +38,14 @@ class FormGroup extends PureComponent {
render() {
const FormComponents = this.props.formComponents;
return (
<div className="form-section">
{this.props.name === 'default' ? null : this.renderHeading()}
<div className={classNames({ 'form-section-collapsed': this.state.collapsed && !this.hasErrors() })}>
{this.props.fields.map(field => (
<Components.FormComponent
<FormComponents.FormComponent
key={field.name}
disabled={this.props.disabled}
{...field}
@ -56,6 +58,7 @@ class FormGroup extends PureComponent {
clearFieldErrors={this.props.clearFieldErrors}
formType={this.props.formType}
currentUser={this.props.currentUser}
formComponents={FormComponents}
/>
))}
</div>

View file

@ -19,6 +19,8 @@ class FormIntl extends PureComponent {
render() {
const FormComponents = this.props.formComponents;
// do not pass FormIntl's own value, inputProperties, and intlInput props down
const properties = omit(this.props, 'value', 'inputProperties', 'intlInput', 'nestedInput');
@ -26,7 +28,7 @@ class FormIntl extends PureComponent {
<div className="form-intl">
{Locales.map((locale, i) => (
<div className={`form-intl-${locale.id}`} key={locale.id}>
<Components.FormComponent {...properties} label={this.props.getLabel(this.props.name, locale.id)} path={this.getLocalePath(i)} locale={locale.id} />
<FormComponents.FormComponent {...properties} label={this.props.getLabel(this.props.name, locale.id)} path={this.getLocalePath(i)} locale={locale.id} />
</div>
))}
</div>

View file

@ -30,7 +30,8 @@ class FormNestedArray extends PureComponent {
const value = this.getCurrentValue()
// do not pass FormNested's own value, input and inputProperties props down
const properties = _.omit(this.props, 'value', 'input', 'inputProperties', 'nestedInput');
const { errors, path } = this.props;
const { errors, path, formComponents } = this.props;
const FormComponents = formComponents;
// only keep errors specific to the nested array (and not its subfields)
const nestedArrayErrors = errors.filter(error => error.path && error.path === path);
const hasErrors = nestedArrayErrors && nestedArrayErrors.length;
@ -42,7 +43,7 @@ class FormNestedArray extends PureComponent {
{value.map(
(subDocument, i) =>
!this.isDeleted(i) && (
<Components.FormNestedItem
<FormComponents.FormNestedItem
{...properties}
key={i}
itemIndex={i}
@ -56,7 +57,7 @@ class FormNestedArray extends PureComponent {
<Components.Button size="small" variant="success" onClick={this.addItem} className="form-nested-button">
<Components.IconAdd height={12} width={12} />
</Components.Button>
{hasErrors ? <Components.FieldErrors errors={nestedArrayErrors} /> : null}
{hasErrors ? <FormComponents.FieldErrors errors={nestedArrayErrors} /> : null}
</div>
</div>
);

View file

@ -3,13 +3,14 @@ import PropTypes from 'prop-types';
import { Components, registerComponent } from 'meteor/vulcan:core';
const FormNestedItem = ({ nestedFields, name, path, removeItem, itemIndex, ...props }, { errors }) => {
const FormComponents = props.formComponents;
const isArray = typeof itemIndex !== 'undefined';
return (
<div className="form-nested-item">
<div className="form-nested-item-inner">
{nestedFields.map((field, i) => {
return (
<Components.FormComponent
<FormComponents.FormComponent
key={i}
{...props}
{...field}

View file

@ -4,6 +4,7 @@ import { Components, registerComponent } from 'meteor/vulcan:core';
class FormNestedObject extends PureComponent {
render() {
const FormComponents = this.props.formComponents;
//const value = this.getCurrentValue()
// do not pass FormNested's own value, input and inputProperties props down
const properties = _.omit(this.props, 'value', 'input', 'inputProperties', 'nestedInput');
@ -15,8 +16,8 @@ class FormNestedObject extends PureComponent {
<div className={`form-group row form-nested ${hasErrors ? 'input-error' : ''}`}>
<label className="control-label col-sm-3">{this.props.label}</label>
<div className="col-sm-9">
<Components.FormNestedItem {...properties} path={`${this.props.path}`} />
{hasErrors ? <Components.FieldErrors errors={nestedObjectErrors} /> : null}
<FormComponents.FormNestedItem {...properties} path={`${this.props.path}`} />
{hasErrors ? <FormComponents.FieldErrors errors={nestedObjectErrors} /> : null}
</div>
</div>
);

View file

@ -52,8 +52,11 @@ class FormComponentInner extends PureComponent {
showCharsRemaining,
charsRemaining,
renderComponent,
formComponents,
} = this.props;
const FormComponents = formComponents;
const hasErrors = errors && errors.length;
const inputName = typeof input === 'function' ? input.name : input;
@ -72,7 +75,7 @@ class FormComponentInner extends PureComponent {
<div className={inputClass}>
{instantiateComponent(beforeComponent, properties)}
<FormInput {...properties}/>
{hasErrors ? <Components.FieldErrors errors={errors} /> : null}
{hasErrors ? <FormComponents.FieldErrors errors={errors} /> : null}
{this.renderClear()}
{showCharsRemaining && (
<div className={classNames('form-control-limit', { danger: charsRemaining < 10 })}>{charsRemaining}</div>