mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Add logic to React FormBuilder to utilise validation schema generated
This commit is contained in:
parent
1142757c21
commit
67f00302f9
101
admin/client/dist/js/bundle.js
vendored
101
admin/client/dist/js/bundle.js
vendored
@ -59,64 +59,69 @@ return this.fetch(e,{method:"put",credentials:"same-origin",body:s(t),headers:n}
|
||||
return this.fetch(e,{method:"delete",credentials:"same-origin",body:s(t),headers:n}).then(a)}}]),e}(),O=new P
|
||||
t["default"]=O},function(e,t,n){n(8),e.exports=self.fetch.bind(self)},function(e,t){!function(e){"use strict"
|
||||
function t(e){if("string"!=typeof e&&(e=String(e)),/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(e))throw new TypeError("Invalid character in header field name")
|
||||
return e.toLowerCase()}function n(e){return"string"!=typeof e&&(e=String(e)),e}function i(e){this.map={},e instanceof i?e.forEach(function(e,t){this.append(t,e)},this):e&&Object.getOwnPropertyNames(e).forEach(function(t){
|
||||
this.append(t,e[t])},this)}function r(e){return e.bodyUsed?Promise.reject(new TypeError("Already read")):void(e.bodyUsed=!0)}function o(e){return new Promise(function(t,n){e.onload=function(){t(e.result)
|
||||
return e.toLowerCase()}function n(e){return"string"!=typeof e&&(e=String(e)),e}function i(e){var t={next:function(){var t=e.shift()
|
||||
return{done:void 0===t,value:t}}}
|
||||
return m.iterable&&(t[Symbol.iterator]=function(){return t}),t}function r(e){this.map={},e instanceof r?e.forEach(function(e,t){this.append(t,e)},this):e&&Object.getOwnPropertyNames(e).forEach(function(t){
|
||||
this.append(t,e[t])},this)}function o(e){return e.bodyUsed?Promise.reject(new TypeError("Already read")):void(e.bodyUsed=!0)}function a(e){return new Promise(function(t,n){e.onload=function(){t(e.result)
|
||||
|
||||
},e.onerror=function(){n(e.error)}})}function a(e){var t=new FileReader
|
||||
return t.readAsArrayBuffer(e),o(t)}function s(e){var t=new FileReader
|
||||
return t.readAsText(e),o(t)}function l(){return this.bodyUsed=!1,this._initBody=function(e){if(this._bodyInit=e,"string"==typeof e)this._bodyText=e
|
||||
else if(h.blob&&Blob.prototype.isPrototypeOf(e))this._bodyBlob=e
|
||||
else if(h.formData&&FormData.prototype.isPrototypeOf(e))this._bodyFormData=e
|
||||
else if(e){if(!h.arrayBuffer||!ArrayBuffer.prototype.isPrototypeOf(e))throw new Error("unsupported BodyInit type")}else this._bodyText=""
|
||||
this.headers.get("content-type")||("string"==typeof e?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type&&this.headers.set("content-type",this._bodyBlob.type))
|
||||
},e.onerror=function(){n(e.error)}})}function s(e){var t=new FileReader
|
||||
return t.readAsArrayBuffer(e),a(t)}function l(e){var t=new FileReader
|
||||
return t.readAsText(e),a(t)}function u(){return this.bodyUsed=!1,this._initBody=function(e){if(this._bodyInit=e,"string"==typeof e)this._bodyText=e
|
||||
else if(m.blob&&Blob.prototype.isPrototypeOf(e))this._bodyBlob=e
|
||||
else if(m.formData&&FormData.prototype.isPrototypeOf(e))this._bodyFormData=e
|
||||
else if(m.searchParams&&URLSearchParams.prototype.isPrototypeOf(e))this._bodyText=e.toString()
|
||||
else if(e){if(!m.arrayBuffer||!ArrayBuffer.prototype.isPrototypeOf(e))throw new Error("unsupported BodyInit type")}else this._bodyText=""
|
||||
this.headers.get("content-type")||("string"==typeof e?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):m.searchParams&&URLSearchParams.prototype.isPrototypeOf(e)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))
|
||||
|
||||
},h.blob?(this.blob=function(){var e=r(this)
|
||||
},m.blob?(this.blob=function(){var e=o(this)
|
||||
if(e)return e
|
||||
if(this._bodyBlob)return Promise.resolve(this._bodyBlob)
|
||||
if(this._bodyFormData)throw new Error("could not read FormData body as blob")
|
||||
return Promise.resolve(new Blob([this._bodyText]))},this.arrayBuffer=function(){return this.blob().then(a)},this.text=function(){var e=r(this)
|
||||
return Promise.resolve(new Blob([this._bodyText]))},this.arrayBuffer=function(){return this.blob().then(s)},this.text=function(){var e=o(this)
|
||||
if(e)return e
|
||||
if(this._bodyBlob)return s(this._bodyBlob)
|
||||
if(this._bodyBlob)return l(this._bodyBlob)
|
||||
if(this._bodyFormData)throw new Error("could not read FormData body as text")
|
||||
return Promise.resolve(this._bodyText)}):this.text=function(){var e=r(this)
|
||||
return e?e:Promise.resolve(this._bodyText)},h.formData&&(this.formData=function(){return this.text().then(d)}),this.json=function(){return this.text().then(JSON.parse)},this}function u(e){var t=e.toUpperCase()
|
||||
return Promise.resolve(this._bodyText)}):this.text=function(){var e=o(this)
|
||||
return e?e:Promise.resolve(this._bodyText)},m.formData&&(this.formData=function(){return this.text().then(f)}),this.json=function(){return this.text().then(JSON.parse)},this}function c(e){var t=e.toUpperCase()
|
||||
|
||||
|
||||
return m.indexOf(t)>-1?t:e}function c(e,t){t=t||{}
|
||||
return g.indexOf(t)>-1?t:e}function d(e,t){t=t||{}
|
||||
var n=t.body
|
||||
if(c.prototype.isPrototypeOf(e)){if(e.bodyUsed)throw new TypeError("Already read")
|
||||
this.url=e.url,this.credentials=e.credentials,t.headers||(this.headers=new i(e.headers)),this.method=e.method,this.mode=e.mode,n||(n=e._bodyInit,e.bodyUsed=!0)}else this.url=e
|
||||
if(this.credentials=t.credentials||this.credentials||"omit",!t.headers&&this.headers||(this.headers=new i(t.headers)),this.method=u(t.method||this.method||"GET"),this.mode=t.mode||this.mode||null,this.referrer=null,
|
||||
if(d.prototype.isPrototypeOf(e)){if(e.bodyUsed)throw new TypeError("Already read")
|
||||
this.url=e.url,this.credentials=e.credentials,t.headers||(this.headers=new r(e.headers)),this.method=e.method,this.mode=e.mode,n||(n=e._bodyInit,e.bodyUsed=!0)}else this.url=e
|
||||
if(this.credentials=t.credentials||this.credentials||"omit",!t.headers&&this.headers||(this.headers=new r(t.headers)),this.method=c(t.method||this.method||"GET"),this.mode=t.mode||this.mode||null,this.referrer=null,
|
||||
("GET"===this.method||"HEAD"===this.method)&&n)throw new TypeError("Body not allowed for GET or HEAD requests")
|
||||
this._initBody(n)}function d(e){var t=new FormData
|
||||
this._initBody(n)}function f(e){var t=new FormData
|
||||
return e.trim().split("&").forEach(function(e){if(e){var n=e.split("="),i=n.shift().replace(/\+/g," "),r=n.join("=").replace(/\+/g," ")
|
||||
t.append(decodeURIComponent(i),decodeURIComponent(r))}}),t}function f(e){var t=new i,n=e.getAllResponseHeaders().trim().split("\n")
|
||||
t.append(decodeURIComponent(i),decodeURIComponent(r))}}),t}function p(e){var t=new r,n=(e.getAllResponseHeaders()||"").trim().split("\n")
|
||||
return n.forEach(function(e){var n=e.trim().split(":"),i=n.shift().trim(),r=n.join(":").trim()
|
||||
t.append(i,r)}),t}function p(e,t){t||(t={}),this.type="default",this.status=t.status,this.ok=this.status>=200&&this.status<300,this.statusText=t.statusText,this.headers=t.headers instanceof i?t.headers:new i(t.headers),
|
||||
this.url=t.url||"",this._initBody(e)}if(!e.fetch){i.prototype.append=function(e,i){e=t(e),i=n(i)
|
||||
t.append(i,r)}),t}function h(e,t){t||(t={}),this.type="default",this.status=t.status,this.ok=this.status>=200&&this.status<300,this.statusText=t.statusText,this.headers=t.headers instanceof r?t.headers:new r(t.headers),
|
||||
this.url=t.url||"",this._initBody(e)}if(!e.fetch){var m={searchParams:"URLSearchParams"in e,iterable:"Symbol"in e&&"iterator"in Symbol,blob:"FileReader"in e&&"Blob"in e&&function(){try{return new Blob,
|
||||
!0}catch(e){return!1}}(),formData:"FormData"in e,arrayBuffer:"ArrayBuffer"in e}
|
||||
r.prototype.append=function(e,i){e=t(e),i=n(i)
|
||||
var r=this.map[e]
|
||||
r||(r=[],this.map[e]=r),r.push(i)},i.prototype["delete"]=function(e){delete this.map[t(e)]},i.prototype.get=function(e){var n=this.map[t(e)]
|
||||
return n?n[0]:null},i.prototype.getAll=function(e){return this.map[t(e)]||[]},i.prototype.has=function(e){return this.map.hasOwnProperty(t(e))},i.prototype.set=function(e,i){this.map[t(e)]=[n(i)]},i.prototype.forEach=function(e,t){
|
||||
Object.getOwnPropertyNames(this.map).forEach(function(n){this.map[n].forEach(function(i){e.call(t,i,n,this)},this)},this)}
|
||||
var h={blob:"FileReader"in e&&"Blob"in e&&function(){try{return new Blob,!0}catch(e){return!1}}(),formData:"FormData"in e,arrayBuffer:"ArrayBuffer"in e},m=["DELETE","GET","HEAD","OPTIONS","POST","PUT"]
|
||||
|
||||
|
||||
c.prototype.clone=function(){return new c(this)},l.call(c.prototype),l.call(p.prototype),p.prototype.clone=function(){return new p(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new i(this.headers),
|
||||
url:this.url})},p.error=function(){var e=new p(null,{status:0,statusText:""})
|
||||
r||(r=[],this.map[e]=r),r.push(i)},r.prototype["delete"]=function(e){delete this.map[t(e)]},r.prototype.get=function(e){var n=this.map[t(e)]
|
||||
return n?n[0]:null},r.prototype.getAll=function(e){return this.map[t(e)]||[]},r.prototype.has=function(e){return this.map.hasOwnProperty(t(e))},r.prototype.set=function(e,i){this.map[t(e)]=[n(i)]},r.prototype.forEach=function(e,t){
|
||||
Object.getOwnPropertyNames(this.map).forEach(function(n){this.map[n].forEach(function(i){e.call(t,i,n,this)},this)},this)},r.prototype.keys=function(){var e=[]
|
||||
return this.forEach(function(t,n){e.push(n)}),i(e)},r.prototype.values=function(){var e=[]
|
||||
return this.forEach(function(t){e.push(t)}),i(e)},r.prototype.entries=function(){var e=[]
|
||||
return this.forEach(function(t,n){e.push([n,t])}),i(e)},m.iterable&&(r.prototype[Symbol.iterator]=r.prototype.entries)
|
||||
var g=["DELETE","GET","HEAD","OPTIONS","POST","PUT"]
|
||||
d.prototype.clone=function(){return new d(this)},u.call(d.prototype),u.call(h.prototype),h.prototype.clone=function(){return new h(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new r(this.headers),
|
||||
url:this.url})},h.error=function(){var e=new h(null,{status:0,statusText:""})
|
||||
return e.type="error",e}
|
||||
var g=[301,302,303,307,308]
|
||||
p.redirect=function(e,t){if(g.indexOf(t)===-1)throw new RangeError("Invalid status code")
|
||||
return new p(null,{status:t,headers:{location:e}})},e.Headers=i,e.Request=c,e.Response=p,e.fetch=function(e,t){return new Promise(function(n,i){function r(){return"responseURL"in a?a.responseURL:/^X-Request-URL:/m.test(a.getAllResponseHeaders())?a.getResponseHeader("X-Request-URL"):void 0
|
||||
var v=[301,302,303,307,308]
|
||||
h.redirect=function(e,t){if(v.indexOf(t)===-1)throw new RangeError("Invalid status code")
|
||||
return new h(null,{status:t,headers:{location:e}})},e.Headers=r,e.Request=d,e.Response=h,e.fetch=function(e,t){return new Promise(function(n,i){function r(){return"responseURL"in a?a.responseURL:/^X-Request-URL:/m.test(a.getAllResponseHeaders())?a.getResponseHeader("X-Request-URL"):void 0
|
||||
|
||||
}var o
|
||||
o=c.prototype.isPrototypeOf(e)&&!t?e:new c(e,t)
|
||||
o=d.prototype.isPrototypeOf(e)&&!t?e:new d(e,t)
|
||||
var a=new XMLHttpRequest
|
||||
a.onload=function(){var e=1223===a.status?204:a.status
|
||||
if(e<100||e>599)return void i(new TypeError("Network request failed"))
|
||||
var t={status:e,statusText:a.statusText,headers:f(a),url:r()},o="response"in a?a.response:a.responseText
|
||||
n(new p(o,t))},a.onerror=function(){i(new TypeError("Network request failed"))},a.open(o.method,o.url,!0),"include"===o.credentials&&(a.withCredentials=!0),"responseType"in a&&h.blob&&(a.responseType="blob"),
|
||||
o.headers.forEach(function(e,t){a.setRequestHeader(t,e)}),a.send("undefined"==typeof o._bodyInit?null:o._bodyInit)})},e.fetch.polyfill=!0}}("undefined"!=typeof self?self:this)},function(e,t,n){var i;(function(t,r){
|
||||
!function(t,n){e.exports=n()}(this,function(){"use strict"
|
||||
a.onload=function(){var e={status:a.status,statusText:a.statusText,headers:p(a),url:r()},t="response"in a?a.response:a.responseText
|
||||
n(new h(t,e))},a.onerror=function(){i(new TypeError("Network request failed"))},a.ontimeout=function(){i(new TypeError("Network request failed"))},a.open(o.method,o.url,!0),"include"===o.credentials&&(a.withCredentials=!0),
|
||||
"responseType"in a&&m.blob&&(a.responseType="blob"),o.headers.forEach(function(e,t){a.setRequestHeader(t,e)}),a.send("undefined"==typeof o._bodyInit?null:o._bodyInit)})},e.fetch.polyfill=!0}}("undefined"!=typeof self?self:this)
|
||||
|
||||
},function(e,t,n){var i;(function(t,r){!function(t,n){e.exports=n()}(this,function(){"use strict"
|
||||
function e(e){return"function"==typeof e||"object"==typeof e&&null!==e}function o(e){return"function"==typeof e}function a(e){K=e}function s(e){Y=e}function l(){return function(){return t.nextTick(p)}}
|
||||
function u(){return function(){Q(p)}}function c(){var e=0,t=new ee(p),n=document.createTextNode("")
|
||||
return t.observe(n,{characterData:!0}),function(){n.data=e=++e%2}}function d(){var e=new MessageChannel
|
||||
@ -342,17 +347,17 @@ e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,wri
|
||||
value:!0}),t.schemaPropType=t.basePropTypes=void 0
|
||||
var l=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]
|
||||
for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},u=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n]
|
||||
i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}(),c=n(4),d=i(c),f=n(20),p=i(f),h=n(30),m=i(h),g=n(31),v=i(g),y=n(32),b=i(y),w=n(16),_=i(w),C=function(e){
|
||||
i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}(),c=n(4),d=i(c),f=n(16),p=i(f),h=n(30),m=i(h),g=n(20),v=i(g),y=n(31),b=i(y),w=n(32),_=i(w),C=function(e){
|
||||
function t(e){o(this,t)
|
||||
var n=a(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e)),i=e.schema.schema
|
||||
return n.state={submittingAction:null},n.submitApi=v["default"].createEndpointFetcher({url:i.attributes.action,method:i.attributes.method}),n.mapActionsToComponents=n.mapActionsToComponents.bind(n),n.mapFieldsToComponents=n.mapFieldsToComponents.bind(n),
|
||||
return n.state={submittingAction:null},n.submitApi=b["default"].createEndpointFetcher({url:i.attributes.action,method:i.attributes.method}),n.mapActionsToComponents=n.mapActionsToComponents.bind(n),n.mapFieldsToComponents=n.mapFieldsToComponents.bind(n),
|
||||
n.handleSubmit=n.handleSubmit.bind(n),n.handleAction=n.handleAction.bind(n),n.buildComponent=n.buildComponent.bind(n),n}return s(t,e),u(t,[{key:"handleAction",value:function n(e){"function"==typeof this.props.handleAction&&this.props.handleAction(e,(0,
|
||||
m["default"])())
|
||||
var t=e.currentTarget.name
|
||||
e.isPropagationStopped()||this.setState({submittingAction:t})}},{key:"handleSubmit",value:function i(e){var t=this,n=this.state.submittingAction?this.state.submittingAction:this.props.schema.schema.actions[0].name,i=l({},e,r({},n,1)),o={
|
||||
"X-Formschema-Request":"state,schema","X-Requested-With":"XMLHttpRequest"},a=function s(e){return t.submitApi(e||i,o).then(function(e){return t.setState({submittingAction:null}),e})["catch"](function(e){
|
||||
throw t.setState({submittingAction:null}),e})}
|
||||
return"function"==typeof this.props.handleSubmit?this.props.handleSubmit(i,n,a):a()}},{key:"buildComponent",value:function c(e){var t=e,n=null!==t.schemaComponent?b["default"].getComponentByName(t.schemaComponent):b["default"].getComponentByDataType(t.type)
|
||||
return"function"==typeof this.props.handleSubmit?this.props.handleSubmit(i,n,a):a()}},{key:"buildComponent",value:function c(e){var t=e,n=null!==t.schemaComponent?_["default"].getComponentByName(t.schemaComponent):_["default"].getComponentByDataType(t.type)
|
||||
|
||||
|
||||
if(null===n)return null
|
||||
@ -362,21 +367,21 @@ var i=this.props.createFn
|
||||
return"function"==typeof i?i(n,t):d["default"].createElement(n,l({key:t.id},t))}},{key:"mapFieldsToComponents",value:function f(e){var t=this,n=this.props.baseFieldComponent
|
||||
return e.map(function(e){var i=e
|
||||
return e.children&&(i=l({},e,{children:t.mapFieldsToComponents(e.children)})),"Structural"===e.type||e.readOnly===!0?t.buildComponent(i):d["default"].createElement(n,l({key:i.id},i,{component:t.buildComponent
|
||||
}))})}},{key:"mapActionsToComponents",value:function p(e){var t=this
|
||||
}))})}},{key:"mapActionsToComponents",value:function h(e){var t=this
|
||||
return e.map(function(e){var n=l({},e)
|
||||
return e.children?n.children=t.mapActionsToComponents(e.children):(n.handleClick=t.handleAction,t.props.submitting&&t.state.submittingAction===e.name&&(n.loading=!0)),t.buildComponent(n)})}},{key:"mergeFieldData",
|
||||
value:function h(e,t){return"undefined"==typeof t?e:_["default"].recursive(!0,e,{data:t.data,source:t.source,message:t.message,valid:t.valid,value:t.value})}},{key:"normalizeFields",value:function g(e,t){
|
||||
value:function g(e,t){return"undefined"==typeof t?e:p["default"].recursive(!0,e,{data:t.data,source:t.source,message:t.message,valid:t.valid,value:t.value})}},{key:"normalizeFields",value:function v(e,t){
|
||||
var n=this
|
||||
return e.map(function(e){var i=t&&t.fields?t.fields.find(function(t){return t.id===e.id}):{},r=_["default"].recursive(!0,n.mergeFieldData(e,i),{schemaComponent:e.component})
|
||||
return e.map(function(e){var i=t&&t.fields?t.fields.find(function(t){return t.id===e.id}):{},r=p["default"].recursive(!0,n.mergeFieldData(e,i),{schemaComponent:e.component})
|
||||
return e.children&&(r.children=n.normalizeFields(e.children,t)),r})}},{key:"normalizeActions",value:function y(e){var t=this
|
||||
return e.map(function(e){var n=_["default"].recursive(!0,e,{schemaComponent:e.component})
|
||||
return e.map(function(e){var n=p["default"].recursive(!0,e,{schemaComponent:e.component})
|
||||
return e.children&&(n.children=t.normalizeActions(e.children)),n})}},{key:"render",value:function w(){var e=this.props.schema.schema,t=this.props.schema.state,n=this.props.baseFormComponent,i=l({},e.attributes,{
|
||||
className:e.attributes["class"],encType:e.attributes.enctype})
|
||||
delete i["class"],delete i.enctype
|
||||
var r=this.props,o=r.asyncValidate,a=r.onSubmitFail,s=r.onSubmitSuccess,u=r.shouldAsyncValidate,c=r.touchOnBlur,f=r.touchOnChange,p=r.persistentSubmitErrors,h=r.validate,g=r.form,v={form:g,fields:this.normalizeFields(e.fields,t),
|
||||
actions:this.normalizeActions(e.actions),attributes:i,data:e.data,initialValues:(0,m["default"])(e,t),onSubmit:this.handleSubmit,valid:t&&t.valid,messages:t&&Array.isArray(t.messages)?t.messages:[],mapActionsToComponents:this.mapActionsToComponents,
|
||||
mapFieldsToComponents:this.mapFieldsToComponents,asyncValidate:o,onSubmitFail:a,onSubmitSuccess:s,shouldAsyncValidate:u,touchOnBlur:c,touchOnChange:f,persistentSubmitErrors:p,validate:h}
|
||||
return d["default"].createElement(n,v)}}]),t}(p["default"]),T=c.PropTypes.shape({id:c.PropTypes.string.isRequired,schema:c.PropTypes.shape({attributes:c.PropTypes.shape({"class":c.PropTypes.string,enctype:c.PropTypes.string
|
||||
return d["default"].createElement(n,v)}}]),t}(v["default"]),T=c.PropTypes.shape({id:c.PropTypes.string.isRequired,schema:c.PropTypes.shape({attributes:c.PropTypes.shape({"class":c.PropTypes.string,enctype:c.PropTypes.string
|
||||
}),fields:c.PropTypes.array.isRequired}).isRequired,state:c.PropTypes.shape({fields:c.PropTypes.array})}),E={createFn:c.PropTypes.func,handleSubmit:c.PropTypes.func,handleAction:c.PropTypes.func,asyncValidate:c.PropTypes.func,
|
||||
onSubmitFail:c.PropTypes.func,onSubmitSuccess:c.PropTypes.func,shouldAsyncValidate:c.PropTypes.func,touchOnBlur:c.PropTypes.bool,touchOnChange:c.PropTypes.bool,persistentSubmitErrors:c.PropTypes.bool,validate:c.PropTypes.func,
|
||||
baseFormComponent:c.PropTypes.func.isRequired,baseFieldComponent:c.PropTypes.func.isRequired}
|
||||
|
@ -26,6 +26,7 @@ require('expose?ReactAddonsCssTransitionGroup!react-addons-css-transition-group'
|
||||
require('expose?ReactAddonsTestUtils!react-addons-test-utils');
|
||||
require('expose?Page!page.js');
|
||||
require('expose?BootstrapCollapse!bootstrap/dist/js/umd/collapse.js');
|
||||
require('expose?approvejs!approvejs');
|
||||
|
||||
require('../../../thirdparty/jquery-ondemand/jquery.ondemand.js');
|
||||
require('../../../thirdparty/jquery-ui/jquery-ui.js');
|
||||
|
@ -12,7 +12,7 @@ function fieldHolder(Field) {
|
||||
*
|
||||
* @returns {Component}
|
||||
*/
|
||||
getDescription() {
|
||||
renderDescription() {
|
||||
if (this.props.description === null) {
|
||||
return null;
|
||||
}
|
||||
@ -29,10 +29,11 @@ function fieldHolder(Field) {
|
||||
*
|
||||
* @returns {Component}
|
||||
*/
|
||||
getMessage() {
|
||||
const message = (this.props.meta) ? this.props.meta.error : null;
|
||||
renderMessage() {
|
||||
const meta = this.props.meta;
|
||||
const message = (meta) ? meta.error : null;
|
||||
|
||||
if (!message) {
|
||||
if (!message || (meta && !meta.touched)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -46,7 +47,7 @@ function fieldHolder(Field) {
|
||||
*
|
||||
* @returns {Component}
|
||||
*/
|
||||
getLeftTitle() {
|
||||
renderLeftTitle() {
|
||||
const labelText = this.props.leftTitle !== null
|
||||
? this.props.leftTitle
|
||||
: this.props.title;
|
||||
@ -67,7 +68,7 @@ function fieldHolder(Field) {
|
||||
*
|
||||
* @returns {Component}
|
||||
*/
|
||||
getRightTitle() {
|
||||
renderRightTitle() {
|
||||
if (!this.props.rightTitle || this.props.hideLabels) {
|
||||
return null;
|
||||
}
|
||||
@ -108,13 +109,13 @@ function fieldHolder(Field) {
|
||||
render() {
|
||||
return (
|
||||
<FormGroup {...this.getHolderProps()}>
|
||||
{this.getLeftTitle()}
|
||||
{this.renderLeftTitle()}
|
||||
<div className="form__field-holder">
|
||||
<Field {...this.props} />
|
||||
{this.getMessage()}
|
||||
{this.getDescription()}
|
||||
{this.renderMessage()}
|
||||
{this.renderDescription()}
|
||||
</div>
|
||||
{this.getRightTitle()}
|
||||
{this.renderRightTitle()}
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import approve from 'approvejs';
|
||||
import merge from 'merge';
|
||||
import schemaFieldValues, { findField } from 'lib/schemaFieldValues';
|
||||
import SilverStripeComponent from 'lib/SilverStripeComponent';
|
||||
import schemaFieldValues from 'lib/schemaFieldValues';
|
||||
import backend from 'lib/Backend';
|
||||
import injector from 'lib/Injector';
|
||||
import merge from 'merge';
|
||||
|
||||
class FormBuilder extends SilverStripeComponent {
|
||||
|
||||
@ -21,6 +22,82 @@ class FormBuilder extends SilverStripeComponent {
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleAction = this.handleAction.bind(this);
|
||||
this.buildComponent = this.buildComponent.bind(this);
|
||||
this.validateForm = this.validateForm.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run validation for every field on the form and return an object which list issues while
|
||||
* validating
|
||||
*
|
||||
* @param values
|
||||
* @returns {*}
|
||||
*/
|
||||
validateForm(values) {
|
||||
if (typeof this.props.validate === 'function') {
|
||||
return this.props.validate(values);
|
||||
}
|
||||
|
||||
const schema = this.props.schema && this.props.schema.schema;
|
||||
if (!schema) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return Object.entries(values).reduce((prev, curr) => {
|
||||
const [key, value] = curr;
|
||||
const field = findField(this.props.schema.schema.fields, key);
|
||||
|
||||
if (!field.validation) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
const error = approve.value(value, this.getFieldValidationRules(field, values));
|
||||
|
||||
if (error.approved) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
// so if there are multiple errors, it will be listed in html spans
|
||||
const errorHtml = `<span>${error.errors.join('</span><span>')}</span>`;
|
||||
|
||||
return Object.assign({}, prev, {
|
||||
[key]: {
|
||||
type: 'error',
|
||||
value: { html: errorHtml },
|
||||
},
|
||||
});
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates validation rules for a given field
|
||||
*
|
||||
* @param {object} field
|
||||
* @param {object} otherValues
|
||||
* @returns {object}
|
||||
*/
|
||||
getFieldValidationRules(field, otherValues) {
|
||||
if (!field.validation) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const rules = Object.assign({},
|
||||
field.validation,
|
||||
{
|
||||
title: (field.leftTitle !== null) ? field.leftTitle : field.title,
|
||||
}
|
||||
);
|
||||
|
||||
// mutate rules for equality check
|
||||
// currently assumes server provides field name to check against
|
||||
if (typeof rules.equal === 'string') {
|
||||
const equalField = findField(this.props.schema.schema.fields, rules.equal);
|
||||
rules.equal = {
|
||||
value: otherValues[rules.equal],
|
||||
field: (equalField.leftTitle !== null) ? equalField.leftTitle : equalField.title,
|
||||
};
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -32,14 +109,12 @@ class FormBuilder extends SilverStripeComponent {
|
||||
handleAction(event) {
|
||||
// Custom handlers
|
||||
if (typeof this.props.handleAction === 'function') {
|
||||
this.props.handleAction(event, schemaFieldValues());
|
||||
this.props.handleAction(event, this.props.values);
|
||||
}
|
||||
|
||||
const name = event.currentTarget.name;
|
||||
|
||||
// Allow custom handlers to cancel event
|
||||
if (!event.isPropagationStopped()) {
|
||||
this.setState({ submittingAction: name });
|
||||
this.setState({ submittingAction: event.currentTarget.name });
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +147,7 @@ class FormBuilder extends SilverStripeComponent {
|
||||
return formSchema;
|
||||
})
|
||||
.catch((reason) => {
|
||||
// TODO Generic CMS error reporting
|
||||
this.setState({ submittingAction: null });
|
||||
throw reason;
|
||||
});
|
||||
@ -274,7 +350,6 @@ class FormBuilder extends SilverStripeComponent {
|
||||
touchOnBlur,
|
||||
touchOnChange,
|
||||
persistentSubmitErrors,
|
||||
validate,
|
||||
form,
|
||||
} = this.props;
|
||||
|
||||
@ -297,7 +372,7 @@ class FormBuilder extends SilverStripeComponent {
|
||||
touchOnBlur,
|
||||
touchOnChange,
|
||||
persistentSubmitErrors,
|
||||
validate,
|
||||
validate: this.validateForm,
|
||||
};
|
||||
|
||||
return <BaseFormComponent {...props} />;
|
||||
@ -330,6 +405,8 @@ const basePropTypes = {
|
||||
touchOnChange: PropTypes.bool,
|
||||
persistentSubmitErrors: PropTypes.bool,
|
||||
validate: PropTypes.func,
|
||||
values: PropTypes.object,
|
||||
submitting: PropTypes.bool,
|
||||
baseFormComponent: PropTypes.func.isRequired,
|
||||
baseFieldComponent: PropTypes.func.isRequired,
|
||||
};
|
||||
@ -337,7 +414,6 @@ const basePropTypes = {
|
||||
FormBuilder.propTypes = Object.assign({}, basePropTypes, {
|
||||
form: PropTypes.string.isRequired,
|
||||
schema: schemaPropType.isRequired,
|
||||
submitting: PropTypes.bool,
|
||||
});
|
||||
|
||||
export { basePropTypes, schemaPropType };
|
||||
|
@ -82,9 +82,9 @@ describe('FormBuilder', () => {
|
||||
{ id: 'fieldTwo', name: 'fieldTwo' },
|
||||
];
|
||||
props.schema.state.fields = [
|
||||
{ id: 'fieldOne', value: 'valOne' },
|
||||
{ id: 'fieldTwo', value: null },
|
||||
{ id: 'notInSchema', value: 'invalid' },
|
||||
{ id: 'fieldOne', name: 'fieldOne', value: 'valOne' },
|
||||
{ id: 'fieldTwo', name: 'fieldTwo', value: null },
|
||||
{ id: 'notInSchema', name: 'notInSchema', value: 'invalid' },
|
||||
];
|
||||
fieldValues = schemaFieldValues(props.schema.schema, props.schema.state);
|
||||
expect(fieldValues).toEqual({
|
||||
@ -110,9 +110,9 @@ describe('FormBuilder', () => {
|
||||
{ id: 'actionTwo', name: 'actionTwo' },
|
||||
];
|
||||
props.schema.state.fields = [
|
||||
{ id: 'fieldOne', value: 'valOne' },
|
||||
{ id: 'fieldTwo', value: null },
|
||||
{ id: 'notInSchema', value: 'invalid' },
|
||||
{ id: 'fieldOne', name: 'fieldOne', value: 'valOne' },
|
||||
{ id: 'fieldTwo', name: 'fieldTwo', value: null },
|
||||
{ id: 'notInSchema', name: 'notInSchema', value: 'invalid' },
|
||||
];
|
||||
});
|
||||
|
||||
@ -156,35 +156,35 @@ describe('FormBuilder', () => {
|
||||
|
||||
it('should retrieve the field in the shallow fields list', () => {
|
||||
fields = [
|
||||
{ id: 'fieldOne' },
|
||||
{ id: 'fieldTwo' },
|
||||
{ id: 'fieldThree' },
|
||||
{ id: 'fieldFour' },
|
||||
{ id: 'fieldOne', name: 'fieldOne' },
|
||||
{ id: 'fieldTwo', name: 'fieldTwo' },
|
||||
{ id: 'fieldThree', name: 'fieldThree' },
|
||||
{ id: 'fieldFour', name: 'fieldFour' },
|
||||
];
|
||||
const field = findField(fields, 'fieldThree');
|
||||
|
||||
expect(field).toBeTruthy();
|
||||
expect(field.id).toBe('fieldThree');
|
||||
expect(field.name).toBe('fieldThree');
|
||||
});
|
||||
|
||||
it('should retrieve the field that is a grandchild in the fields list', () => {
|
||||
fields = [
|
||||
{ id: 'fieldOne' },
|
||||
{ id: 'fieldTwo', children: [
|
||||
{ id: 'fieldTwoOne' },
|
||||
{ id: 'fieldTwoTwo', children: [
|
||||
{ id: 'fieldTwoOne' },
|
||||
{ id: 'fieldTwoTwo' },
|
||||
{ id: 'fieldTwoThree' },
|
||||
{ id: 'fieldOne', name: 'fieldOne' },
|
||||
{ id: 'fieldTwo', name: 'fieldTwo', children: [
|
||||
{ id: 'fieldTwoOne', name: 'fieldTwoOne' },
|
||||
{ id: 'fieldTwoTwo', name: 'fieldTwoTwo', children: [
|
||||
{ id: 'fieldTwoOne', name: 'fieldTwoOne' },
|
||||
{ id: 'fieldTwoTwo', name: 'fieldTwoTwo' },
|
||||
{ id: 'fieldTwoThree', name: 'fieldTwoThree' },
|
||||
] },
|
||||
] },
|
||||
{ id: 'fieldThree' },
|
||||
{ id: 'fieldFour' },
|
||||
{ id: 'fieldThree', name: 'fieldThree' },
|
||||
{ id: 'fieldFour', name: 'fieldFour' },
|
||||
];
|
||||
const field = findField(fields, 'fieldTwoThree');
|
||||
|
||||
expect(field).toBeTruthy();
|
||||
expect(field.id).toBe('fieldTwoThree');
|
||||
expect(field.name).toBe('fieldTwoThree');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -174,11 +174,15 @@ FormBuilderLoader.defaultProps = {
|
||||
export default connect(
|
||||
(state, ownProps) => {
|
||||
const schema = state.schemas[ownProps.schemaUrl];
|
||||
const form = schema ? schema.id : null;
|
||||
const submitting = state.form
|
||||
&& state.form[ownProps.schemaUrl]
|
||||
&& state.form[ownProps.schemaUrl].submitting;
|
||||
return { schema, form, submitting };
|
||||
const form = schema && schema.id;
|
||||
const reduxFormState = state.form
|
||||
&& state.form[ownProps.schemaUrl];
|
||||
const submitting = reduxFormState
|
||||
&& reduxFormState.submitting;
|
||||
const values = reduxFormState
|
||||
&& reduxFormState.values;
|
||||
|
||||
return { schema, form, submitting, values };
|
||||
},
|
||||
(dispatch) => ({
|
||||
schemaActions: bindActionCreators(schemaActions, dispatch),
|
||||
|
@ -3,22 +3,22 @@
|
||||
* schema's deep nesting of fields.
|
||||
*
|
||||
* @param fields
|
||||
* @param id
|
||||
* @param name
|
||||
* @returns {object|undefined}
|
||||
*/
|
||||
export function findField(fields, id) {
|
||||
export function findField(fields, name) {
|
||||
let result = null;
|
||||
if (!fields) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = fields.find(field => field.id === id);
|
||||
result = fields.find(field => field.name === name);
|
||||
|
||||
for (const field of fields) {
|
||||
if (result) {
|
||||
break;
|
||||
}
|
||||
result = findField(field.children, id);
|
||||
result = findField(field.children, name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -36,7 +36,7 @@ export default function schemaFieldValues(schema, state) {
|
||||
|
||||
return state.fields
|
||||
.reduce((prev, curr) => {
|
||||
const match = findField(schema.fields, curr.id);
|
||||
const match = findField(schema.fields, curr.name);
|
||||
|
||||
if (!match) {
|
||||
return prev;
|
||||
|
4
npm-shrinkwrap.json
generated
4
npm-shrinkwrap.json
generated
@ -4,6 +4,10 @@
|
||||
"npm-shrinkwrap-version": "6.0.1",
|
||||
"node-version": "v4.5.0",
|
||||
"dependencies": {
|
||||
"approvejs": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/approvejs/-/approvejs-1.1.2.tgz"
|
||||
},
|
||||
"autoprefixer": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.4.1.tgz",
|
||||
|
@ -33,6 +33,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/silverstripe/silverstripe-framework#readme",
|
||||
"dependencies": {
|
||||
"approvejs": "^1.1.2",
|
||||
"babel-polyfill": "^6.7.4",
|
||||
"blueimp-file-upload": "6.0.3",
|
||||
"blueimp-load-image": "1.1.3",
|
||||
|
Loading…
x
Reference in New Issue
Block a user