2016-11-26 11:33:27 +09:00
/ *
Main form component .
This component expects :
# # # All Forms :
- collection
- currentUser
- client ( Apollo client )
# # # New Form :
- newMutation
# # # Edit Form :
- editMutation
- removeMutation
- document
* /
2018-05-08 20:09:42 -04:00
import {
2018-05-11 09:52:04 +09:00
registerComponent ,
Components ,
runCallbacks ,
getCollection ,
getErrors ,
getSetting ,
Utils ,
isIntlField ,
2018-05-08 20:09:42 -04:00
} from 'meteor/vulcan:core' ;
2017-06-01 10:00:16 +09:00
import React , { Component } from 'react' ;
import PropTypes from 'prop-types' ;
2017-10-17 09:35:41 -04:00
import { intlShape } from 'meteor/vulcan:i18n' ;
2018-06-21 10:28:34 +09:00
import Formsy from 'formsy-react' ;
2018-03-26 14:27:45 +09:00
import { getEditableFields , getInsertableFields } from '../modules/utils.js' ;
2018-03-24 11:16:11 +09:00
import cloneDeep from 'lodash/cloneDeep' ;
2018-08-07 10:13:06 +09:00
import get from 'lodash/get' ;
2018-03-24 11:16:11 +09:00
import set from 'lodash/set' ;
import unset from 'lodash/unset' ;
import compact from 'lodash/compact' ;
import update from 'lodash/update' ;
2018-03-26 14:27:45 +09:00
import merge from 'lodash/merge' ;
2018-05-08 20:09:42 -04:00
import find from 'lodash/find' ;
2018-05-23 16:02:36 -04:00
import pick from 'lodash/pick' ;
2018-08-09 11:41:06 +09:00
import isEqual from 'lodash/isEqual' ;
2018-05-08 20:09:42 -04:00
import isEqualWith from 'lodash/isEqualWith' ;
2018-07-10 10:02:08 +02:00
import uniq from 'lodash/uniq' ;
2018-08-05 10:30:56 +09:00
import uniqBy from 'lodash/uniqBy' ;
2018-08-06 10:29:06 +09:00
import isObject from 'lodash/isObject' ;
2018-09-05 10:44:56 +09:00
import mapValues from 'lodash/mapValues' ;
import pickBy from 'lodash/pickBy' ;
2018-05-08 20:09:42 -04:00
2018-03-26 14:27:45 +09:00
import { convertSchema , formProperties } from '../modules/schema_utils' ;
2018-08-07 16:42:45 +09:00
import { isEmptyValue } from '../modules/utils' ;
2018-06-29 13:01:56 +02:00
import { getParentPath } from '../modules/path_utils' ;
2018-03-24 11:16:11 +09:00
2018-08-07 10:13:06 +09:00
const compactParent = ( object , path ) => {
const parentPath = getParentPath ( path ) ;
2018-05-23 17:09:32 +09:00
// note: we only want to compact arrays, not objects
const compactIfArray = x => Array . isArray ( x ) ? compact ( x ) : x ;
update ( object , parentPath , compactIfArray ) ;
2018-03-24 11:33:28 +09:00
} ;
2016-11-23 17:22:29 +09:00
2018-09-05 10:44:56 +09:00
const getDefaultValues = convertedSchema => {
// TODO: make this work with nested schemas, too
return pickBy ( mapValues ( convertedSchema , field => field . defaultValue ) , value => value ) ;
}
2018-08-09 11:41:06 +09:00
const getInitialStateFromProps = ( nextProps ) => {
2018-04-20 14:35:53 -05:00
const collection = nextProps . collection || getCollection ( nextProps . collectionName ) ;
const schema = collection . simpleSchema ( ) ;
2018-09-05 10:44:56 +09:00
const convertedSchema = convertSchema ( schema ) ;
const formType = nextProps . document ? 'edit' : 'new' ;
// for new document forms, add default values to initial document
const defaultValues = formType === 'new' ? getDefaultValues ( convertedSchema ) : { } ;
const initialDocument = merge ( { } , defaultValues , nextProps . prefilledProps , nextProps . document ) ;
2018-08-17 19:02:44 +09:00
// remove all instances of the `__typename` property from document
Utils . removeProperty ( initialDocument , '__typename' ) ;
2018-04-20 14:35:53 -05:00
return {
2018-08-09 11:41:06 +09:00
disabled : false ,
errors : [ ] ,
deletedValues : [ ] ,
currentValues : { } ,
2018-04-20 14:35:53 -05:00
// convert SimpleSchema schema into JSON object
2018-09-05 10:44:56 +09:00
schema : convertedSchema ,
2018-04-20 14:35:53 -05:00
// Also store all field schemas (including nested schemas) in a flat structure
flatSchema : convertSchema ( schema , true ) ,
// the initial document passed as props
2018-08-07 16:42:45 +09:00
initialDocument ,
// initialize the current document to be the same as the initial document
currentDocument : initialDocument ,
2018-04-20 14:35:53 -05:00
} ;
} ;
2016-11-23 17:22:29 +09:00
/ *
1. Constructor
2. Helpers
3. Errors
4. Context
4. Method & Callback
5. Render
* /
2018-06-20 10:23:54 +09:00
class SmartForm extends Component {
2018-05-11 09:52:04 +09:00
constructor ( props ) {
2018-03-25 12:13:30 +09:00
super ( props ) ;
this . state = {
2018-08-09 11:41:06 +09:00
... getInitialStateFromProps ( props ) ,
2018-03-25 12:13:30 +09:00
} ;
}
2018-04-10 17:23:23 +09:00
defaultValues = { } ;
2018-03-24 11:21:39 +09:00
submitFormCallbacks = [ ] ;
successFormCallbacks = [ ] ;
failureFormCallbacks = [ ] ;
2016-11-23 17:22:29 +09:00
// --------------------------------------------------------------------- //
// ------------------------------- Helpers ----------------------------- //
// --------------------------------------------------------------------- //
2018-03-24 11:33:28 +09:00
/ *
Get the current collection
* /
2018-03-24 11:21:39 +09:00
getCollection = ( ) => {
2018-01-26 17:41:15 -06:00
return this . props . collection || getCollection ( this . props . collectionName ) ;
2018-03-24 11:33:28 +09:00
} ;
/ *
2018-07-10 10:02:08 +02:00
Get current typeName
* /
getTypeName = ( ) => {
return this . getCollection ( ) . options . typeName ;
}
/ *
2018-03-24 11:33:28 +09:00
If a document is being passed , this is an edit form
* /
getFormType = ( ) => {
return this . props . document ? 'edit' : 'new' ;
} ;
/ *
2018-07-10 10:02:08 +02:00
Get a list of all insertable fields
* /
2018-07-24 18:29:27 +02:00
getInsertableFields = ( schema ) => {
return getInsertableFields ( schema || this . state . schema , this . props . currentUser ) ;
2018-07-10 10:02:08 +02:00
}
/ *
Get a list of all editable fields
* /
2018-07-24 18:29:27 +02:00
getEditableFields = ( schema ) => {
return getEditableFields ( schema || this . state . schema , this . props . currentUser , this . state . initialDocument )
2018-07-10 10:02:08 +02:00
}
/ *
Get a list of all mutable ( insertable / editable depending on current form type ) fields
* /
2018-07-24 18:29:27 +02:00
getMutableFields = ( schema ) => {
return this . getFormType ( ) === 'edit' ? this . getEditableFields ( schema ) : this . getInsertableFields ( schema ) ;
2018-07-10 10:02:08 +02:00
}
/ *
2018-04-10 17:23:23 +09:00
Get the current document
2018-03-24 11:33:28 +09:00
* /
getDocument = ( ) => {
2018-08-07 16:42:45 +09:00
return this . state . currentDocument ;
2018-03-24 11:33:28 +09:00
} ;
/ *
2018-04-20 14:35:53 -05:00
Like getDocument , but cross - reference with getFieldNames ( )
2018-03-24 11:33:28 +09:00
to only return fields that actually need to be submitted
2018-04-20 14:35:53 -05:00
Also remove any deleted values .
2018-07-10 10:02:08 +02:00
2018-03-24 11:33:28 +09:00
* /
2018-07-10 10:02:08 +02:00
getData = ( customArgs ) => {
const args = { excludeHiddenFields : false , replaceIntlFields : true , addExtraFields : false , ... customArgs } ;
2018-07-24 18:29:27 +02:00
2018-03-24 11:33:28 +09:00
// only keep relevant fields
2018-05-21 09:42:08 +09:00
// for intl fields, make sure we look in foo_intl and not foo
2018-07-10 10:02:08 +02:00
const fields = this . getFieldNames ( args ) ;
2018-05-23 16:02:36 -04:00
let data = pick ( this . getDocument ( ) , ... fields ) ;
2018-03-24 11:33:28 +09:00
2018-08-07 10:13:06 +09:00
// compact deleted values
2018-03-24 11:33:28 +09:00
this . state . deletedValues . forEach ( path => {
2018-08-05 11:17:46 +09:00
if ( path . includes ( '.' ) ) {
/ *
2018-08-07 10:13:06 +09:00
If deleted field is a nested field , nested array , or nested array item , try to compact its parent array
2018-08-05 11:17:46 +09:00
- Nested field : 'address.city'
- Nested array : 'addresses.1'
- Nested array item : 'addresses.1.city'
* /
2018-09-05 17:08:29 +02:00
compactParent ( data , path ) ;
2018-08-05 11:17:46 +09:00
}
2018-08-07 10:13:06 +09:00
} ) ;
2018-03-24 11:33:28 +09:00
// run data object through submitForm callbacks
data = runCallbacks ( this . submitFormCallbacks , data ) ;
return data ;
} ;
// --------------------------------------------------------------------- //
// -------------------------------- Fields ----------------------------- //
// --------------------------------------------------------------------- //
/ *
Get all field groups
2018-03-22 19:22:54 +09:00
2018-03-24 11:33:28 +09:00
* /
getFieldGroups = ( ) => {
2018-03-22 19:22:54 +09:00
// build fields array by iterating over the list of field names
2018-03-26 11:51:08 +09:00
let fields = this . getFieldNames ( ) . map ( fieldName => {
2018-03-22 19:22:54 +09:00
// get schema for the current field
2018-04-20 14:35:53 -05:00
return this . createField ( fieldName , this . state . schema ) ;
2016-11-23 17:22:29 +09:00
} ) ;
2018-03-24 11:33:28 +09:00
fields = _ . sortBy ( fields , 'order' ) ;
2016-11-23 17:22:29 +09:00
2016-12-12 09:55:24 +09:00
// get list of all unique groups (based on their name) used in current fields
2018-08-05 10:30:56 +09:00
let groups = _ . compact ( uniqBy ( _ . pluck ( fields , 'group' ) , g => g && g . name ) ) ;
2016-11-23 17:22:29 +09:00
// for each group, add relevant fields
groups = groups . map ( group => {
2018-03-24 11:33:28 +09:00
group . label = group . label || this . context . intl . formatMessage ( { id : group . name } ) ;
group . fields = _ . filter ( fields , field => {
return field . group && field . group . name === group . name ;
} ) ;
2016-11-23 17:22:29 +09:00
return group ;
} ) ;
// add default group
2018-03-24 11:33:28 +09:00
groups = [
{
name : 'default' ,
label : 'default' ,
order : 0 ,
fields : _ . filter ( fields , field => {
return ! field . group ;
} ) ,
} ,
] . concat ( groups ) ;
2016-11-23 17:22:29 +09:00
// sort by order
2018-03-24 11:33:28 +09:00
groups = _ . sortBy ( groups , 'order' ) ;
2016-11-23 17:22:29 +09:00
// console.log(groups);
return groups ;
2018-03-24 11:33:28 +09:00
} ;
2016-11-23 17:22:29 +09:00
2018-03-24 11:33:28 +09:00
/ *
2018-07-10 10:02:08 +02:00
2018-03-24 11:33:28 +09:00
Get a list of the fields to be included in the current form
2016-11-23 17:22:29 +09:00
2018-07-10 10:02:08 +02:00
Note : when submitting the form ( getData ( ) ) , do not include any extra fields .
2018-03-24 11:33:28 +09:00
* /
2018-04-07 11:53:40 +09:00
getFieldNames = ( args = { } ) => {
2018-07-24 18:29:27 +02:00
2018-07-10 10:02:08 +02:00
const { schema = this . state . schema , excludeHiddenFields = true , replaceIntlFields = false , addExtraFields = true } = args ;
2018-04-07 11:53:40 +09:00
2018-07-10 10:02:08 +02:00
const { fields , addFields , } = this . props ;
2016-11-23 17:22:29 +09:00
// get all editable/insertable fields (depending on current form type)
2018-07-24 18:29:27 +02:00
let relevantFields = this . getMutableFields ( schema ) ;
2016-11-23 17:22:29 +09:00
// if "fields" prop is specified, restrict list of fields to it
2018-03-24 11:33:28 +09:00
if ( typeof fields !== 'undefined' && fields . length > 0 ) {
2016-11-23 17:22:29 +09:00
relevantFields = _ . intersection ( relevantFields , fields ) ;
2017-10-01 11:49:19 +09:00
}
2018-07-10 10:02:08 +02:00
// if "removeFields" prop is specified, remove its fields
const removeFields = this . props . hideFields || this . props . removeFields ;
if ( typeof removeFields !== 'undefined' && removeFields . length > 0 ) {
relevantFields = _ . difference ( relevantFields , removeFields ) ;
}
// if "addFields" prop is specified, add its fields
if ( addExtraFields && typeof addFields !== 'undefined' && addFields . length > 0 ) {
relevantFields = relevantFields . concat ( addFields ) ;
2016-11-23 17:22:29 +09:00
}
2018-04-07 10:09:38 +09:00
// remove all hidden fields
if ( excludeHiddenFields ) {
2018-05-08 20:09:42 -04:00
const document = this . getDocument ( ) ;
2018-04-07 10:09:38 +09:00
relevantFields = _ . reject ( relevantFields , fieldName => {
const hidden = schema [ fieldName ] . hidden ;
2018-05-08 20:09:42 -04:00
return typeof hidden === 'function' ? hidden ( { ... this . props , document } ) : hidden ;
2018-04-07 10:09:38 +09:00
} ) ;
}
2018-05-21 09:42:08 +09:00
// replace intl fields
if ( replaceIntlFields ) {
relevantFields = relevantFields . map ( fieldName => isIntlField ( schema [ fieldName ] ) ? ` ${ fieldName } _intl ` : fieldName ) ;
}
2018-07-10 10:02:08 +02:00
// remove any duplicates
relevantFields = uniq ( relevantFields ) ;
2016-11-23 17:22:29 +09:00
return relevantFields ;
2018-03-24 11:33:28 +09:00
} ;
2016-11-23 17:22:29 +09:00
2018-03-26 14:27:45 +09:00
/ *
2018-04-20 14:35:53 -05:00
Given a field ' s name , the containing schema , and parent , create the
2018-03-26 14:27:45 +09:00
complete field object to be passed to the component
* /
createField = ( fieldName , schema , parentFieldName , parentPath ) => {
const fieldPath = parentPath ? ` ${ parentPath } . ${ fieldName } ` : fieldName ;
const fieldSchema = schema [ fieldName ] ;
// intialize properties
let field = {
... _ . pick ( fieldSchema , formProperties ) ,
2018-04-20 14:35:53 -05:00
document : this . state . initialDocument ,
2018-03-26 14:27:45 +09:00
name : fieldName ,
path : fieldPath ,
datatype : fieldSchema . type ,
layout : this . props . layout ,
2018-04-14 18:09:35 +09:00
input : fieldSchema . input || fieldSchema . control ,
2018-03-26 14:27:45 +09:00
} ;
2018-05-07 17:41:22 +09:00
// if this an intl'd field, use a special intlInput
2018-05-08 12:23:42 +09:00
if ( isIntlField ( fieldSchema ) ) {
2018-05-07 17:41:22 +09:00
field . intlInput = true ;
}
2018-04-10 17:23:23 +09:00
if ( field . defaultValue ) {
set ( this . defaultValues , fieldPath , field . defaultValue ) ;
}
2018-03-26 14:27:45 +09:00
// if field has a parent field, pass it on
if ( parentFieldName ) {
field . parentFieldName = parentFieldName ;
}
field . label = this . getLabel ( fieldName ) ;
// // replace value by prefilled value if value is empty
// const prefill = fieldSchema.prefill || (fieldSchema.form && fieldSchema.form.prefill);
// if (prefill) {
// const prefilledValue = typeof prefill === 'function' ? prefill.call(fieldSchema) : prefill;
// if (!!prefilledValue && !field.value) {
// field.prefilledValue = prefilledValue;
// field.value = prefilledValue;
// }
// }
// if options are a function, call it
if ( typeof field . options === 'function' ) {
field . options = field . options . call ( fieldSchema , this . props ) ;
}
2018-03-26 17:50:03 +09:00
// add any properties specified in fieldSchema.form as extra props passed on
// to the form component, calling them if they are functions
2018-07-10 10:02:08 +02:00
const inputProperties = fieldSchema . form || fieldSchema . inputProperties || { } ;
for ( const prop in inputProperties ) {
const property = inputProperties [ prop ] ;
field [ prop ] = typeof property === 'function' ? property . call ( fieldSchema , this . props ) : property ;
}
// if field is not creatable/updatable, disable it
2018-07-24 18:42:59 +02:00
if ( ! this . getMutableFields ( schema ) . includes ( fieldName ) ) {
2018-07-10 10:02:08 +02:00
field . disabled = true ;
2018-03-26 14:27:45 +09:00
}
// add description as help prop
if ( fieldSchema . description ) {
field . help = fieldSchema . description ;
}
2018-04-20 16:25:11 +02:00
// nested fields: set input to "nested"
2018-03-26 14:27:45 +09:00
if ( fieldSchema . schema ) {
field . nestedSchema = fieldSchema . schema ;
2018-05-23 22:04:32 +09:00
field . nestedInput = true ;
2018-07-26 17:07:48 +02:00
2018-03-26 14:27:45 +09:00
// get nested schema
// for each nested field, get field object by calling createField recursively
2018-04-07 10:09:38 +09:00
field . nestedFields = this . getFieldNames ( { schema : field . nestedSchema } ) . map ( subFieldName => {
2018-03-26 14:27:45 +09:00
return this . createField ( subFieldName , field . nestedSchema , fieldName , fieldPath ) ;
} ) ;
}
return field ;
} ;
2018-03-24 11:33:28 +09:00
/ *
2018-03-24 11:16:11 +09:00
2018-04-20 14:35:53 -05:00
Get a field ' s label
2018-03-24 11:16:11 +09:00
2018-04-20 14:35:53 -05:00
* /
2018-07-02 16:59:29 +02:00
getLabel = ( fieldName , fieldLocale ) => {
const collectionName = this . getCollection ( ) . options . collectionName . toLowerCase ( ) ;
2018-09-19 21:12:27 -04:00
const defaultMessage = '|*|*|' ;
let id = ` ${ collectionName } . ${ fieldName } ` ;
let intlLabel ;
intlLabel = this . context . intl . formatMessage ( { id , defaultMessage } ) ;
if ( intlLabel === defaultMessage ) {
id = ` global. ${ fieldName } ` ;
intlLabel = this . context . intl . formatMessage ( { id } ) ;
if ( intlLabel === defaultMessage ) {
id = fieldName ;
intlLabel = this . context . intl . formatMessage ( { id } ) ;
}
}
2018-07-02 16:59:29 +02:00
const schemaLabel = this . state . flatSchema [ fieldName ] && this . state . flatSchema [ fieldName ] . label ;
const label = intlLabel || schemaLabel || fieldName ;
if ( fieldLocale ) {
const intlFieldLocale = this . context . intl . formatMessage ( { id : ` locales. ${ fieldLocale } ` , defaultMessage : fieldLocale } ) ;
return ` ${ label } ( ${ intlFieldLocale } ) ` ;
} else {
return label ;
}
2018-03-24 11:33:28 +09:00
} ;
2018-03-24 11:16:11 +09:00
2018-03-24 11:33:28 +09:00
// --------------------------------------------------------------------- //
// ------------------------------- Errors ------------------------------ //
// --------------------------------------------------------------------- //
2017-08-16 16:18:40 +09:00
2018-04-09 13:10:42 +09:00
/ *
2018-04-20 14:35:53 -05:00
2018-04-09 13:10:42 +09:00
Add error to form state
Errors can have the following properties :
- id : used as an internationalization key , for example ` errors.required `
- path : for field - specific errors , the path of the field with the issue
2018-04-21 17:57:53 +09:00
- properties : additional data . Will be passed to vulcan - i18n as values
2018-04-14 18:09:35 +09:00
- message : if id cannot be used as i81n key , message will be used
2018-04-21 17:57:53 +09:00
2018-04-09 13:10:42 +09:00
* /
2018-03-24 11:33:28 +09:00
throwError = error => {
2018-04-28 10:54:03 +09:00
let formErrors = getErrors ( error ) ;
2018-04-09 13:10:42 +09:00
2018-01-25 15:03:03 -06:00
// eslint-disable-next-line no-console
2018-03-29 11:58:24 +09:00
console . log ( formErrors ) ;
2018-04-09 13:10:42 +09:00
2018-03-29 11:58:24 +09:00
// add error(s) to state
2017-02-02 15:15:51 +01:00
this . setState ( prevState => ( {
2018-03-29 11:58:24 +09:00
errors : [ ... prevState . errors , ... formErrors ] ,
2017-02-02 15:15:51 +01:00
} ) ) ;
2018-03-24 11:33:28 +09:00
} ;
2016-11-23 17:22:29 +09:00
2018-04-14 17:21:10 +09:00
/ *
Clear errors for a field
* /
clearFieldErrors = path => {
const errors = this . state . errors . filter ( error => error . path !== path ) ;
this . setState ( { errors } ) ;
2018-04-21 17:57:53 +09:00
} ;
2018-04-14 17:21:10 +09:00
2018-03-26 14:27:45 +09:00
// --------------------------------------------------------------------- //
// ------------------------------- Context ----------------------------- //
// --------------------------------------------------------------------- //
2017-05-06 16:08:01 +09:00
// add something to deleted values
2018-03-24 11:33:28 +09:00
addToDeletedValues = name => {
2017-05-06 16:08:01 +09:00
this . setState ( prevState => ( {
2018-03-24 11:33:28 +09:00
deletedValues : [ ... prevState . deletedValues , name ] ,
2017-05-06 16:08:01 +09:00
} ) ) ;
2018-03-24 11:33:28 +09:00
} ;
2017-05-06 16:08:01 +09:00
2017-05-30 09:49:38 +09:00
// add a callback to the form submission
2018-03-24 11:33:28 +09:00
addToSubmitForm = callback => {
2017-05-30 09:49:38 +09:00
this . submitFormCallbacks . push ( callback ) ;
2018-03-24 11:33:28 +09:00
} ;
2017-05-30 09:49:38 +09:00
2017-07-06 12:49:28 -07:00
// add a callback to form submission success
2018-03-24 11:33:28 +09:00
addToSuccessForm = callback => {
2017-07-06 12:49:28 -07:00
this . successFormCallbacks . push ( callback ) ;
2018-03-24 11:33:28 +09:00
} ;
2017-07-06 12:49:28 -07:00
// add a callback to form submission failure
2018-03-24 11:33:28 +09:00
addToFailureForm = callback => {
2017-07-06 12:49:28 -07:00
this . failureFormCallbacks . push ( callback ) ;
2018-03-24 11:33:28 +09:00
} ;
2017-07-06 12:49:28 -07:00
2018-03-24 11:33:28 +09:00
setFormState = fn => {
2017-04-20 16:04:24 +09:00
this . setState ( fn ) ;
2018-03-24 11:33:28 +09:00
} ;
2017-04-20 16:04:24 +09:00
2018-03-24 11:33:28 +09:00
submitFormContext = newValues => {
2017-09-08 22:52:54 -07:00
// keep the previous ones and extend (with possible replacement) with new ones
2018-03-24 11:33:28 +09:00
this . setState (
prevState => ( {
currentValues : {
... prevState . currentValues ,
... newValues ,
} , // Submit form after setState update completed
} ) ,
2018-06-30 11:34:17 +02:00
( ) => this . submitForm ( this . form . getModel ( ) )
2018-03-24 11:33:28 +09:00
) ;
} ;
2017-09-08 22:52:54 -07:00
2016-11-23 17:22:29 +09:00
// pass on context to all child components
2018-03-24 11:21:39 +09:00
getChildContext = ( ) => {
2016-11-23 17:22:29 +09:00
return {
throwError : this . throwError ,
2017-02-02 15:15:51 +01:00
clearForm : this . clearForm ,
2018-05-08 20:09:42 -04:00
refetchForm : this . refetchForm ,
isChanged : this . isChanged ,
submitForm : this . submitFormContext , //Change in name because we already have a function
2018-05-11 09:52:04 +09:00
// called submitForm, but no reason for the user to know
// about that
2017-05-06 16:08:01 +09:00
addToDeletedValues : this . addToDeletedValues ,
2017-01-23 15:50:55 +01:00
updateCurrentValues : this . updateCurrentValues ,
2016-11-23 17:22:29 +09:00
getDocument : this . getDocument ,
2018-06-27 19:53:21 +02:00
getLabel : this . getLabel ,
2018-04-20 14:35:53 -05:00
initialDocument : this . state . initialDocument ,
2017-04-20 16:04:24 +09:00
setFormState : this . setFormState ,
2017-06-01 11:50:47 +09:00
addToSubmitForm : this . addToSubmitForm ,
2017-07-06 12:49:28 -07:00
addToSuccessForm : this . addToSuccessForm ,
addToFailureForm : this . addToFailureForm ,
2018-03-25 10:54:45 +09:00
errors : this . state . errors ,
2018-03-25 12:13:30 +09:00
currentValues : this . state . currentValues ,
deletedValues : this . state . deletedValues ,
2016-11-23 17:22:29 +09:00
} ;
2018-03-24 11:33:28 +09:00
} ;
2016-11-23 17:22:29 +09:00
// --------------------------------------------------------------------- //
2018-03-24 11:33:28 +09:00
// ------------------------------ Lifecycle ---------------------------- //
2016-11-23 17:22:29 +09:00
// --------------------------------------------------------------------- //
2018-08-09 11:41:06 +09:00
/ *
When props change , reinitialize state
2018-08-10 13:08:12 +09:00
// TODO: only need to check nextProps.prefilledProps?
2018-08-09 11:41:06 +09:00
// TODO: see https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
* /
UNSAFE _componentWillReceiveProps ( nextProps ) {
if ( ! isEqual ( this . props , nextProps ) ) {
this . setState ( getInitialStateFromProps ( nextProps ) ) ;
}
2018-04-20 14:35:53 -05:00
}
2018-03-24 11:33:28 +09:00
/ *
2018-05-08 20:09:42 -04:00
2018-04-20 14:35:53 -05:00
Manually update the current values of one or more fields ( i . e . on change or blur ) .
2018-05-08 20:09:42 -04:00
2018-03-24 11:33:28 +09:00
* /
2018-08-07 10:13:06 +09:00
updateCurrentValues = ( newValues , options = { } ) => {
// default to overwriting old value with new
const { mode = 'overwrite' } = options ;
2018-09-05 17:08:29 +02:00
2018-03-24 11:33:28 +09:00
// keep the previous ones and extend (with possible replacement) with new ones
this . setState ( prevState => {
2018-09-05 17:08:29 +02:00
2018-08-09 11:41:06 +09:00
// keep only the relevant properties
const { currentValues , currentDocument , deletedValues } = cloneDeep ( prevState ) ;
const newState = { currentValues , currentDocument , deletedValues , foo : { } } ;
2018-03-24 11:33:28 +09:00
Object . keys ( newValues ) . forEach ( key => {
const path = key ;
const value = newValues [ key ] ;
2018-08-07 16:42:45 +09:00
if ( isEmptyValue ( value ) ) {
2018-03-24 11:33:28 +09:00
// delete value
2018-03-26 14:27:45 +09:00
unset ( newState . currentValues , path ) ;
2018-08-07 16:42:45 +09:00
set ( newState . currentDocument , path , null ) ;
2018-03-28 11:51:18 +09:00
newState . deletedValues = [ ... prevState . deletedValues , path ] ;
2018-03-24 11:33:28 +09:00
} else {
2018-08-07 10:13:06 +09:00
// 1. update currentValues
2018-03-25 12:13:30 +09:00
set ( newState . currentValues , path , value ) ;
2018-08-07 10:13:06 +09:00
// 2. update currentDocument
// For arrays and objects, give option to merge instead of overwrite
if ( mode === 'merge' && ( Array . isArray ( value ) || isObject ( value ) ) ) {
2018-08-07 16:42:45 +09:00
const oldValue = get ( newState . currentDocument , path ) ;
set ( newState . currentDocument , path , merge ( oldValue , value ) ) ;
2018-08-07 10:13:06 +09:00
} else {
2018-08-07 16:42:45 +09:00
set ( newState . currentDocument , path , value ) ;
2018-08-07 10:13:06 +09:00
}
// 3. in case value had previously been deleted, "undelete" it
2018-03-28 11:51:18 +09:00
newState . deletedValues = _ . without ( prevState . deletedValues , path ) ;
2018-03-24 11:33:28 +09:00
}
} ) ;
2018-03-25 12:13:30 +09:00
return newState ;
2018-03-24 11:33:28 +09:00
} ) ;
} ;
2018-05-11 09:52:04 +09:00
2018-05-08 20:09:42 -04:00
/ *
Warn the user if there are unsaved changes
* /
handleRouteLeave = ( ) => {
if ( this . isChanged ( ) ) {
const message = this . context . intl . formatMessage ( {
id : 'forms.confirm_discard' ,
2018-05-11 09:52:04 +09:00
defaultMessage : 'Are you sure you want to discard your changes?' ,
2018-05-08 20:09:42 -04:00
} ) ;
return message ;
}
} ;
2018-07-24 18:29:27 +02:00
2018-05-22 09:51:36 +02:00
//see https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload
//the message returned is actually ignored by most browsers and a default message 'Are you sure you want to leave this page? You might have unsaved changes' is displayed. See the Notes section on the mozilla docs above
handlePageLeave = ( event ) => {
2018-07-24 18:29:27 +02:00
if ( this . isChanged ( ) ) {
2018-05-22 09:51:36 +02:00
const message = this . context . intl . formatMessage ( {
id : 'forms.confirm_discard' ,
defaultMessage : 'Are you sure you want to discard your changes?'
} ) ;
if ( event ) {
event . returnValue = message ;
}
2018-07-24 18:29:27 +02:00
2018-05-22 09:51:36 +02:00
return message ;
}
} ;
2018-07-24 18:29:27 +02:00
2018-05-08 20:09:42 -04:00
/ *
Install a route leave hook to warn the user if there are unsaved changes
* /
componentDidMount = ( ) => {
let warnUnsavedChanges = getSetting ( 'forms.warnUnsavedChanges' ) ;
if ( typeof this . props . warnUnsavedChanges === 'boolean' ) {
warnUnsavedChanges = this . props . warnUnsavedChanges ;
}
if ( warnUnsavedChanges ) {
const routes = this . props . router . routes ;
const currentRoute = routes [ routes . length - 1 ] ;
this . props . router . setRouteLeaveHook ( currentRoute , this . handleRouteLeave ) ;
2018-07-24 18:29:27 +02:00
2018-05-22 09:51:36 +02:00
//check for closing the browser with unsaved changes
window . onbeforeunload = this . handlePageLeave ;
2018-05-08 20:09:42 -04:00
}
} ;
2018-05-11 09:52:04 +09:00
2018-05-22 09:51:36 +02:00
/ *
Remove the closing browser check on component unmount
see https : //gist.github.com/mknabe/bfcb6db12ef52323954a28655801792d
* /
componentWillUnmount = ( ) => {
let warnUnsavedChanges = getSetting ( 'forms.warnUnsavedChanges' ) ;
if ( typeof this . props . warnUnsavedChanges === 'boolean' ) {
warnUnsavedChanges = this . props . warnUnsavedChanges ;
}
if ( warnUnsavedChanges ) {
window . onbeforeunload = undefined ; //undefined instead of null to support IE
}
} ;
2018-07-24 18:29:27 +02:00
2018-04-20 14:35:53 -05:00
/ *
2018-05-08 20:09:42 -04:00
Returns true if there are any differences between the initial document and the current one
* /
isChanged = ( ) => {
const initialDocument = this . state . initialDocument ;
const changedDocument = this . getDocument ( ) ;
2018-05-11 09:52:04 +09:00
2018-05-08 20:09:42 -04:00
const changedValue = find ( changedDocument , ( value , key , collection ) => {
return ! isEqualWith ( value , initialDocument [ key ] , ( objValue , othValue ) => {
if ( ! objValue && ! othValue ) return true ;
} ) ;
} ) ;
2018-05-11 09:52:04 +09:00
2018-05-08 20:09:42 -04:00
return typeof changedValue !== 'undefined' ;
} ;
2018-05-11 09:52:04 +09:00
2018-05-08 20:09:42 -04:00
/ *
Refetch the document from the database ( in case it was updated by another process or to reset the form )
* /
refetchForm = ( ) => {
if ( this . props . data && this . props . data . refetch ) {
this . props . data . refetch ( ) ;
}
} ;
2018-05-11 09:52:04 +09:00
2018-05-08 20:09:42 -04:00
/ *
2018-03-24 11:33:28 +09:00
Clear and reset the form
By default , clear errors and keep current values and deleted values
2018-07-04 11:25:44 +02:00
On form success we ' ll clear current values too . Note : document includes currentValues
2018-03-24 11:33:28 +09:00
* /
2018-05-11 09:52:04 +09:00
clearForm = ( { clearErrors = true , clearCurrentValues = false , clearDeletedValues = false , document } ) => {
2018-05-08 20:09:42 -04:00
document = document ? merge ( { } , this . props . prefilledProps , document ) : null ;
2018-05-11 09:52:04 +09:00
2018-03-24 11:33:28 +09:00
this . setState ( prevState => ( {
errors : clearErrors ? [ ] : prevState . errors ,
currentValues : clearCurrentValues ? { } : prevState . currentValues ,
2018-08-10 14:46:49 +02:00
currentDocument : clearCurrentValues ? { } : prevState . currentDocument ,
2018-03-24 11:33:28 +09:00
deletedValues : clearDeletedValues ? [ ] : prevState . deletedValues ,
2018-07-04 11:25:44 +02:00
initialDocument : document && ! clearCurrentValues ? document : prevState . initialDocument ,
2018-03-24 11:33:28 +09:00
disabled : false ,
} ) ) ;
} ;
/ *
2018-05-08 20:09:42 -04:00
2018-03-24 11:33:28 +09:00
Key down handler
* /
formKeyDown = event => {
if ( ( event . ctrlKey || event . metaKey ) && event . keyCode === 13 ) {
2018-06-30 11:34:17 +02:00
this . submitForm ( this . form . getModel ( ) ) ;
2018-03-24 11:33:28 +09:00
}
} ;
newMutationSuccessCallback = result => {
2016-11-25 12:22:13 +09:00
this . mutationSuccessCallback ( result , 'new' ) ;
2018-03-24 11:33:28 +09:00
} ;
2016-11-25 12:22:13 +09:00
2018-03-24 11:33:28 +09:00
editMutationSuccessCallback = result => {
2016-11-25 12:22:13 +09:00
this . mutationSuccessCallback ( result , 'edit' ) ;
2018-03-24 11:33:28 +09:00
} ;
2016-11-25 12:22:13 +09:00
2018-03-24 11:21:39 +09:00
mutationSuccessCallback = ( result , mutationType ) => {
2018-06-13 18:28:01 +09:00
this . setState ( prevState => ( { disabled : false } ) ) ;
2018-06-20 10:23:54 +09:00
const document = result . data [ Object . keys ( result . data ) [ 0 ] ] . data ; // document is always on first property
2016-11-23 17:22:29 +09:00
2016-11-25 12:22:13 +09:00
// for new mutation, run refetch function if it exists
if ( mutationType === 'new' && this . props . refetch ) this . props . refetch ( ) ;
2018-05-11 09:52:04 +09:00
// call the clear form method (i.e. trigger setState) only if the form has not been unmounted
2018-05-08 20:09:42 -04:00
// (we are in an async callback, everything can happen!)
2018-07-07 18:49:04 +02:00
if ( this . form ) {
2018-06-30 11:34:17 +02:00
this . form . reset ( this . getDocument ( ) ) ;
2018-05-08 20:09:42 -04:00
this . clearForm ( { clearErrors : true , clearCurrentValues : true , clearDeletedValues : true , document } ) ;
2017-01-13 18:17:08 +01:00
}
2017-01-10 17:49:03 +09:00
2017-07-06 12:49:28 -07:00
// run document through mutation success callbacks
result = runCallbacks ( this . successFormCallbacks , result ) ;
2016-11-23 17:22:29 +09:00
// run success callback if it exists
if ( this . props . successCallback ) this . props . successCallback ( document ) ;
2018-03-24 11:33:28 +09:00
} ;
2016-11-23 17:22:29 +09:00
// catch graphql errors
2018-04-30 15:39:33 -03:00
mutationErrorCallback = ( document , error ) => {
2018-03-24 11:33:28 +09:00
this . setState ( prevState => ( { disabled : false } ) ) ;
2016-11-23 17:22:29 +09:00
2018-01-25 15:03:03 -06:00
// eslint-disable-next-line no-console
2018-03-24 11:33:28 +09:00
console . log ( '// graphQL Error' ) ;
2018-01-25 15:03:03 -06:00
// eslint-disable-next-line no-console
console . log ( error ) ;
2017-07-06 12:49:28 -07:00
// run mutation failure callbacks on error, we do not allow the callbacks to change the error
runCallbacks ( this . failureFormCallbacks , error ) ;
2016-11-23 17:22:29 +09:00
if ( ! _ . isEmpty ( error ) ) {
// add error to state
2017-07-07 10:21:15 +09:00
this . throwError ( error ) ;
2016-11-23 17:22:29 +09:00
}
2018-05-11 09:52:04 +09:00
2016-11-23 17:22:29 +09:00
// run error callback if it exists
2018-04-30 15:39:33 -03:00
if ( this . props . errorCallback ) this . props . errorCallback ( document , error ) ;
2018-05-11 09:52:04 +09:00
2018-05-10 10:18:55 +09:00
// scroll back up to show error messages
2018-05-08 20:09:42 -04:00
Utils . scrollIntoView ( '.flash-message' ) ;
2018-03-24 11:33:28 +09:00
} ;
2016-11-23 17:22:29 +09:00
2018-04-20 14:35:53 -05:00
/ *
2018-03-24 11:33:28 +09:00
Submit form handler
2017-06-22 16:41:56 +09:00
2018-03-24 11:33:28 +09:00
* /
submitForm = data => {
2018-03-23 15:46:31 +09:00
// note: we can discard the data collected by Formsy because all the data we need is already available via getDocument()
2017-06-22 16:41:56 +09:00
// if form is disabled (there is already a submit handler running) don't do anything
if ( this . state . disabled ) {
return ;
}
2017-07-06 12:49:28 -07:00
2017-08-16 16:24:50 +09:00
// clear errors and disable form while it's submitting
2018-03-24 11:33:28 +09:00
this . setState ( prevState => ( { errors : [ ] , disabled : true } ) ) ;
2016-11-23 17:22:29 +09:00
// complete the data with values from custom components which are not being catched by Formsy mixin
2016-12-20 09:27:16 +09:00
// note: it follows the same logic as SmartForm's getDocument method
2018-07-10 10:02:08 +02:00
data = this . getData ( { replaceIntlFields : true , addExtraFields : false } ) ;
2016-11-23 17:22:29 +09:00
// if there's a submit callback, run it
if ( this . props . submitCallback ) {
2018-09-05 17:08:29 +02:00
data = this . props . submitCallback ( data ) || data ;
2016-11-23 17:22:29 +09:00
}
2018-03-24 11:33:28 +09:00
if ( this . getFormType ( ) === 'new' ) {
2018-07-10 10:02:08 +02:00
// create document form
this . props [ ` create ${ this . getTypeName ( ) } ` ] ( { data } )
2018-03-24 11:33:28 +09:00
. then ( this . newMutationSuccessCallback )
2018-04-30 15:39:33 -03:00
. catch ( error => this . mutationErrorCallback ( document , error ) ) ;
2018-03-24 11:33:28 +09:00
} else {
2018-07-10 10:02:08 +02:00
// update document form
const documentId = this . getDocument ( ) . _id ;
this . props [ ` update ${ this . getTypeName ( ) } ` ] ( { selector : { documentId } , data } )
2018-03-24 11:33:28 +09:00
. then ( this . editMutationSuccessCallback )
2018-05-02 10:35:13 -03:00
. catch ( error => this . mutationErrorCallback ( document , error ) ) ;
2016-11-23 17:22:29 +09:00
}
2018-03-24 11:33:28 +09:00
} ;
2016-11-23 17:22:29 +09:00
2018-03-24 11:33:28 +09:00
/ *
2016-11-23 17:22:29 +09:00
2018-03-24 11:33:28 +09:00
Delete document handler
* /
2018-03-24 11:21:39 +09:00
deleteDocument = ( ) => {
2016-11-23 17:22:29 +09:00
const document = this . getDocument ( ) ;
2016-11-25 12:22:13 +09:00
const documentId = this . props . document . _id ;
2016-11-27 08:39:25 +09:00
const documentTitle = document . title || document . name || '' ;
2016-11-23 17:22:29 +09:00
2018-03-24 11:33:28 +09:00
const deleteDocumentConfirm = this . context . intl . formatMessage (
{ id : 'forms.delete_confirm' } ,
{ title : documentTitle }
) ;
2016-11-23 17:22:29 +09:00
2016-12-08 23:48:16 +01:00
if ( window . confirm ( deleteDocumentConfirm ) ) {
2018-03-24 11:33:28 +09:00
this . props
. removeMutation ( { documentId } )
. then ( mutationResult => {
// the mutation result looks like {data:{collectionRemove: null}} if succeeded
if ( this . props . removeSuccessCallback ) this . props . removeSuccessCallback ( { documentId , documentTitle } ) ;
2016-11-25 12:22:13 +09:00
if ( this . props . refetch ) this . props . refetch ( ) ;
2016-11-23 17:22:29 +09:00
} )
2018-05-02 13:39:52 -03:00
. catch ( error => {
// eslint-disable-next-line no-console
console . log ( error ) ;
} ) ;
2016-11-23 17:22:29 +09:00
}
2018-03-24 11:33:28 +09:00
} ;
2016-11-23 17:22:29 +09:00
// --------------------------------------------------------------------- //
2018-03-24 11:33:28 +09:00
// ----------------------------- Render -------------------------------- //
2016-11-23 17:22:29 +09:00
// --------------------------------------------------------------------- //
2018-05-11 09:52:04 +09:00
render ( ) {
2016-11-23 17:22:29 +09:00
const fieldGroups = this . getFieldGroups ( ) ;
2018-01-26 17:41:15 -06:00
const collectionName = this . getCollection ( ) . _name ;
2016-11-23 17:22:29 +09:00
return (
2018-03-24 11:33:28 +09:00
< div className = { 'document-' + this . getFormType ( ) } >
2018-07-24 18:29:27 +02:00
< Formsy.Form onSubmit = { this . submitForm } onKeyDown = { this . formKeyDown } ref = { e => { this . form = e ; } } >
2018-05-11 09:52:04 +09:00
< Components.FormErrors errors = { this . state . errors } / >
2018-01-26 17:41:15 -06:00
2018-03-24 11:33:28 +09:00
{ fieldGroups . map ( group => (
2018-03-26 17:50:03 +09:00
< Components.FormGroup
key = { group . name }
{ ... group }
2018-03-28 11:14:36 +09:00
errors = { this . state . errors }
2018-04-06 17:56:25 +09:00
throwError = { this . throwError }
2018-03-27 10:45:17 +09:00
currentValues = { this . state . currentValues }
2018-03-26 17:50:03 +09:00
updateCurrentValues = { this . updateCurrentValues }
2018-04-06 17:56:25 +09:00
deletedValues = { this . state . deletedValues }
addToDeletedValues = { this . addToDeletedValues }
2018-04-14 17:21:10 +09:00
clearFieldErrors = { this . clearFieldErrors }
2018-03-26 17:50:03 +09:00
formType = { this . getFormType ( ) }
2018-04-28 10:54:03 +09:00
currentUser = { this . props . currentUser }
2018-06-30 09:23:49 +02:00
disabled = { this . state . disabled }
2018-03-26 17:50:03 +09:00
/ >
2018-03-24 11:33:28 +09:00
) ) }
2018-01-26 17:41:15 -06:00
2017-11-09 10:01:22 +09:00
{ this . props . repeatErrors && this . renderErrors ( ) }
2018-03-24 11:33:28 +09:00
< Components.FormSubmit
submitLabel = { this . props . submitLabel }
cancelLabel = { this . props . cancelLabel }
2018-05-08 20:09:42 -04:00
revertLabel = { this . props . revertLabel }
2018-03-24 11:33:28 +09:00
cancelCallback = { this . props . cancelCallback }
2018-05-08 20:09:42 -04:00
revertCallback = { this . props . revertCallback }
2018-03-24 11:33:28 +09:00
document = { this . getDocument ( ) }
deleteDocument = { ( this . getFormType ( ) === 'edit' && this . props . showRemove && this . deleteDocument ) || null }
collectionName = { collectionName }
2018-09-04 21:47:05 +09:00
currentValues = { this . state . currentValues }
deletedValues = { this . state . deletedValues }
errors = { this . state . errors }
2017-10-17 09:35:41 -04:00
/ >
2018-06-21 10:28:34 +09:00
< / Formsy.Form >
2016-11-23 17:22:29 +09:00
< / div >
2018-03-24 11:33:28 +09:00
) ;
2016-11-23 17:22:29 +09:00
}
}
2018-06-20 10:23:54 +09:00
SmartForm . propTypes = {
2016-11-23 17:22:29 +09:00
// main options
2017-02-02 15:15:51 +01:00
collection : PropTypes . object ,
2018-01-26 17:41:15 -06:00
collectionName : ( props , propName , componentName ) => {
if ( ! props . collection && ! props . collectionName ) {
return new Error ( ` One of props 'collection' or 'collectionName' was not specified in ' ${ componentName } '. ` ) ;
}
if ( ! props . collection && typeof props [ 'collectionName' ] !== 'string' ) {
return new Error ( ` Prop collectionName was not of type string in ' ${ componentName } ` ) ;
}
} ,
2017-02-02 15:15:51 +01:00
document : PropTypes . object , // if a document is passed, this will be an edit form
schema : PropTypes . object , // usually not needed
2016-11-23 17:22:29 +09:00
// graphQL
2017-02-02 15:15:51 +01:00
newMutation : PropTypes . func , // the new mutation
editMutation : PropTypes . func , // the edit mutation
removeMutation : PropTypes . func , // the remove mutation
2016-11-23 17:22:29 +09:00
// form
2017-02-02 15:15:51 +01:00
prefilledProps : PropTypes . object ,
layout : PropTypes . string ,
fields : PropTypes . arrayOf ( PropTypes . string ) ,
2018-07-10 10:02:08 +02:00
addFields : PropTypes . arrayOf ( PropTypes . string ) ,
removeFields : PropTypes . arrayOf ( PropTypes . string ) ,
hideFields : PropTypes . arrayOf ( PropTypes . string ) , // OpenCRUD backwards compatibility
2017-02-02 15:15:51 +01:00
showRemove : PropTypes . bool ,
2018-09-25 09:42:56 -04:00
submitLabel : PropTypes . node ,
cancelLabel : PropTypes . node ,
revertLabel : PropTypes . node ,
2017-11-09 10:01:22 +09:00
repeatErrors : PropTypes . bool ,
2018-05-08 20:09:42 -04:00
warnUnsavedChanges : PropTypes . bool ,
2016-11-23 17:22:29 +09:00
// callbacks
2017-02-02 15:15:51 +01:00
submitCallback : PropTypes . func ,
successCallback : PropTypes . func ,
removeSuccessCallback : PropTypes . func ,
errorCallback : PropTypes . func ,
cancelCallback : PropTypes . func ,
2018-05-08 20:09:42 -04:00
revertCallback : PropTypes . func ,
2017-02-02 15:15:51 +01:00
currentUser : PropTypes . object ,
client : PropTypes . object ,
2018-03-24 11:33:28 +09:00
} ;
2016-11-23 17:22:29 +09:00
2018-06-20 10:23:54 +09:00
SmartForm . defaultProps = {
2017-11-09 10:01:22 +09:00
layout : 'horizontal' ,
2018-03-23 08:51:24 +09:00
prefilledProps : { } ,
2017-11-09 10:01:22 +09:00
repeatErrors : false ,
2018-03-22 16:54:50 +09:00
showRemove : true ,
2018-03-24 11:33:28 +09:00
} ;
2016-11-23 17:22:29 +09:00
2018-06-20 10:23:54 +09:00
SmartForm . contextTypes = {
2018-03-24 11:33:28 +09:00
intl : intlShape ,
} ;
2016-11-23 17:22:29 +09:00
2018-06-20 10:23:54 +09:00
SmartForm . childContextTypes = {
2017-05-06 16:08:01 +09:00
addToDeletedValues : PropTypes . func ,
2018-03-24 11:16:11 +09:00
deletedValues : PropTypes . array ,
2017-05-30 09:49:38 +09:00
addToSubmitForm : PropTypes . func ,
2017-07-06 12:49:28 -07:00
addToFailureForm : PropTypes . func ,
addToSuccessForm : PropTypes . func ,
2017-02-02 15:15:51 +01:00
updateCurrentValues : PropTypes . func ,
2017-04-20 16:04:24 +09:00
setFormState : PropTypes . func ,
2017-02-02 15:15:51 +01:00
throwError : PropTypes . func ,
clearForm : PropTypes . func ,
2018-05-08 20:09:42 -04:00
refetchForm : PropTypes . func ,
isChanged : PropTypes . func ,
2018-03-26 14:27:45 +09:00
initialDocument : PropTypes . object ,
2017-09-08 22:52:54 -07:00
getDocument : PropTypes . func ,
2018-06-27 19:53:21 +02:00
getLabel : PropTypes . func ,
2017-09-08 22:52:54 -07:00
submitForm : PropTypes . func ,
2018-03-25 10:54:45 +09:00
errors : PropTypes . array ,
2018-03-25 12:13:30 +09:00
currentValues : PropTypes . object ,
2018-03-24 11:33:28 +09:00
} ;
2016-11-23 17:22:29 +09:00
2018-06-20 10:23:54 +09:00
module . exports = SmartForm ;
2018-03-26 17:50:03 +09:00
2018-06-20 10:23:54 +09:00
registerComponent ( 'Form' , SmartForm ) ;