Skip to content

Commit

Permalink
fix: ensure that controlConfig is loaded per formBuilder instance. We…
Browse files Browse the repository at this point in the history
… delay setting the static property controlConfig on class control until we construct the control class in layout.
  • Loading branch information
lucasnetau committed Nov 3, 2023
1 parent e963748 commit 5f8de00
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 12 deletions.
3 changes: 0 additions & 3 deletions src/js/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ export default class Controls {
this.opts = opts
this.dom = d.controls
this.getRegistered = control.getRegistered
// ability for controls to have their own configuration / options
// of the format control identifier (type, or type.subtype): {options}
control.controlConfig = opts.controlConfig || {}
this.init()
}

Expand Down
2 changes: 1 addition & 1 deletion src/js/form-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ function FormBuilder(opts, element, $) {
if (!opts.layout) {
opts.layout = layout
}
const layoutEngine = new opts.layout(opts.layoutTemplates, true, opts.disableHTMLLabels)
const layoutEngine = new opts.layout(opts.layoutTemplates, true, opts.disableHTMLLabels, opts.controlConfig)

const h = new Helpers(formID, layoutEngine, formBuilder)
const m = markup
Expand Down
2 changes: 1 addition & 1 deletion src/js/form-render.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ class FormRender {
const rendered = []

// instantiate the layout class & loop through the field configuration
const engine = new opts.layout(opts.layoutTemplates, false, opts.disableHTMLLabels)
const engine = new opts.layout(opts.layoutTemplates, false, opts.disableHTMLLabels, opts.controlConfig)
if (opts.formData.length) {
for (let i = 0; i < opts.formData.length; i++) {
const fieldData = opts.formData[i]
Expand Down
18 changes: 11 additions & 7 deletions src/js/layout.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// LAYOUT.JS
import utils from './utils'
import { getAllGridRelatedClasses } from './utils'
import control from './control'

const processClassName = (data, field) => {
// wrap the output in a form-group div & return
Expand Down Expand Up @@ -43,12 +44,14 @@ export default class layout {
/**
* Prepare the templates for layout
* @param {Object} templates object containing custom or overwrite templates
* @param {Boolean} preview - are we rendering a preview for the formBuilder stage
* @param {Boolean} disableHTMLLabels - do we render labels as HTML or plain text
* @param {Boolean} [preview=false] - are we rendering a preview for the formBuilder stage
* @param {Boolean} [disableHTMLLabels=false] - do we render labels as HTML or plain text
* @param {Array} [controlConfig={}] - ability for controls to have their own configuration / options of the format control identifier (type, or type.subtype): {options}
*/
constructor(templates, preview = false, disableHTMLLabels = false) {
constructor(templates, preview = false, disableHTMLLabels = false, controlConfig = {}) {
this.preview = preview ?? false
this.disableHTMLLabels = disableHTMLLabels ?? false
this.controlConfig = controlConfig ?? {}

// supported templates for outputting a field
// preferred layout template can be indicated by specifying a 'layout' in the return object of control::build
Expand Down Expand Up @@ -115,8 +118,9 @@ export default class layout {
this.data = jQuery.extend({}, data)

// build the control
const control = new renderControl(data, this.preview)
let field = control.build()
control.controlConfig = this.controlConfig
const controlInstance = new renderControl(data, this.preview)
let field = controlInstance.build()
if (typeof field !== 'object' || !field.field) {
field = { field: field }
}
Expand Down Expand Up @@ -145,10 +149,10 @@ export default class layout {
const element = this.processTemplate(elementTemplate, field.field, label, help)

// execute prerender events
control.on('prerender')(element)
controlInstance.on('prerender')(element)

// bind control on render events
element.addEventListener('fieldRendered', control.on('render'))
element.addEventListener('fieldRendered', controlInstance.on('render'))
return element
}

Expand Down
50 changes: 50 additions & 0 deletions tests/form-builder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -435,4 +435,54 @@ describe('async loading tests', () => {

expect(wrap2.formBuilder('markup', 'div').outerHTML).toBe('<div></div>')
})

test('controlConfig is loaded per instance', async () => {
const wrap1 = $('<div>')
const wrap2 = $('<div>')
const templates = {
text: function(fieldData) {
this.classConfig.callback()
return {
field: '<span>preview</span>'
}
}
}
const cb1 = jest.fn()
const cb2 = jest.fn()
const config1 = {
templates,
controlConfig: {
'text.text': {
callback: cb1
}
}
}
const config2 = {
templates,
controlConfig: {
'text.text': {
callback: cb2
}
}
}
const p1 = wrap1.formBuilder(config1).promise
const p2 = wrap2.formBuilder(config2).promise

const fb1 = await p1
const fb2 = await p2

const field = {
type: 'text',
class: 'form-control'
}
fb1.actions.addField(field)

expect(cb1.mock.calls).toHaveLength(1)
expect(cb2.mock.calls).toHaveLength(0)

fb2.actions.addField(field)
expect(cb1.mock.calls).toHaveLength(1)
expect(cb2.mock.calls).toHaveLength(1)
})

})

0 comments on commit 5f8de00

Please sign in to comment.