From 7aa72ce1f7036858a6ab5cf2d10cae8f40ea7e28 Mon Sep 17 00:00:00 2001 From: James Lucas Date: Thu, 4 Jul 2024 20:12:48 +1000 Subject: [PATCH 1/4] fix: catch control lookup error so that we can output the custom control specific error --- src/js/customControls.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/js/customControls.js b/src/js/customControls.js index 41e07fd44..f6b6434b6 100644 --- a/src/js/customControls.js +++ b/src/js/customControls.js @@ -75,24 +75,24 @@ export default class customControls { // if there is no template defined for this type, check if we already have this type/subtype registered if (!templates[type]) { // check that this type is already registered - const controlClass = control.getClass(type, field.subtype) - if (!controlClass) { - super.error( + try { + const controlClass = control.getClass(type, field.subtype) + + // generate a random key & map the settings against it + lookup = field.datatype ? field.datatype : `${type}-${Math.floor(Math.random() * 9000 + 1000)}` + + this.customRegister[lookup] = jQuery.extend(field, { + type: type, + class: controlClass, + }) + } catch(e) { + control.error( 'Error while registering custom field: ' + type + (field.subtype ? ':' + field.subtype : '') + '. Unable to find any existing defined control or template for rendering.', ) - continue } - - // generate a random key & map the settings against it - lookup = field.datatype ? field.datatype : `${type}-${Math.floor(Math.random() * 9000 + 1000)}` - - this.customRegister[lookup] = jQuery.extend(field, { - type: type, - class: controlClass, - }) } else { //Map the field definition into the templated control class const controlClass = this.templateControlRegister[type] From 04a66c21f18cd3c65e4ca599fb673e3fb20f9d5c Mon Sep 17 00:00:00 2001 From: James Lucas Date: Thu, 4 Jul 2024 20:14:21 +1000 Subject: [PATCH 2/4] fix: use correct definition in custom control label and icon lookup --- src/js/customControls.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/customControls.js b/src/js/customControls.js index f6b6434b6..4a68e412c 100644 --- a/src/js/customControls.js +++ b/src/js/customControls.js @@ -123,7 +123,7 @@ export default class customControls { * @param {Object|Number|String} [args] - string or key/val pairs for string lookups with variables * @return {String} the translated label */ - const def = this.definition + const def = this.def let i18n = def.i18n || {} const locale = mi18n.locale i18n = i18n[locale] || i18n.default || i18n @@ -158,7 +158,7 @@ export default class customControls { icon(type) { // @todo - support for `${css_prefix_text}${attr.name}` - is this for inputSets? Doesnt look like it but can't see anything else that sets attr.name? // https://formbuilder.online/docs/formBuilder/options/inputSets/ - const def = this.definition + const def = this.def if (def && typeof def.icon === 'object') { return def.icon[type] } From a5a7f80b0fa6587698e4546a3b3900a636a29726 Mon Sep 17 00:00:00 2001 From: James Lucas Date: Thu, 4 Jul 2024 20:15:37 +1000 Subject: [PATCH 3/4] fix: translate custom control label if required when setting up the translation map --- src/js/customControls.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/customControls.js b/src/js/customControls.js index 4a68e412c..208368bf8 100644 --- a/src/js/customControls.js +++ b/src/js/customControls.js @@ -104,7 +104,7 @@ export default class customControls { } // map label & icon - this.def.i18n[locale][lookup] = field.label + this.def.i18n[locale][lookup] = Array.isArray(field.label) ? mi18n.get(...field.label) || field.label[0] : field.label this.def.icon[lookup] = field.icon } } From f1b580bd9a1ac3446241bc85841f22982ae64cc1 Mon Sep 17 00:00:00 2001 From: James Lucas Date: Thu, 4 Jul 2024 20:16:50 +1000 Subject: [PATCH 4/4] fix: fix lookup of label for custom controls when constructing control panel and adding field --- src/js/controls.js | 4 +++- src/js/form-builder.js | 2 ++ tests/control/custom.test.js | 28 ++++++++++++++++++++++++++++ tests/translation.test.js | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/js/controls.js b/src/js/controls.js index 54f4ded8e..352b9b7d0 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -66,8 +66,10 @@ export default class Controls { // first check if this is a custom control let custom = this.custom.lookup(type) let controlClass + let label if (custom) { controlClass = custom.class + label = this.custom.label(type) } else { custom = {} @@ -76,9 +78,9 @@ export default class Controls { if (!controlClass || !controlClass.active(type)) { continue } + label = controlClass.label(type) } const icon = custom.icon || controlClass.icon(type) - let label = custom.label || controlClass.label(type) const iconClassName = !icon ? custom.iconClassName || `${css_prefix_text + type.replace(/-[\d]{4}$/, '')}` : '' // if the class has specified a custom icon, inject it into the label diff --git a/src/js/form-builder.js b/src/js/form-builder.js index b44a5c0b1..0d815a062 100644 --- a/src/js/form-builder.js +++ b/src/js/form-builder.js @@ -292,7 +292,9 @@ function FormBuilder(opts, element, $) { // check for a custom type const custom = controls.custom.lookup(field.type) if (custom) { + const customType = field.type field = Object.assign({}, custom) + field.label = controls.custom.label(customType) } else { const controlClass = controls.getClass(field.type) field.label = controlClass.label(field.type) diff --git a/tests/control/custom.test.js b/tests/control/custom.test.js index 48b2caf7a..9ecef5d10 100644 --- a/tests/control/custom.test.js +++ b/tests/control/custom.test.js @@ -404,4 +404,32 @@ describe('Test Custom Control', () => { expect(fbWrap.find('#starRating-1697591966052-0')[0].outerHTML).toBe('') }) + + test('custom replacedField with invalid type throws Error', async () => { + const fbWrap = $('
') + + const replaceFields = [{ + type: 'nonExistent' + }] + + let error + await fbWrap.formBuilder({replaceFields}).promise.catch(e => error = e) + + expect(error).toBeInstanceOf(Error) + expect(error.message).toMatch(/Error while registering custom field:/) + }) + + test('custom replacedField with missing type throws Error', async () => { + const fbWrap = $('
') + + const replaceFields = [{ + value: 1 + }] + + let error + await fbWrap.formBuilder({replaceFields}).promise.catch(e => error = e) + + expect(error).toBeInstanceOf(Error) + expect(error.message).toMatch(/Ignoring invalid custom field definition. Please specify a type property./) + }) }) \ No newline at end of file diff --git a/tests/translation.test.js b/tests/translation.test.js index 307e7e67a..0c677e151 100644 --- a/tests/translation.test.js +++ b/tests/translation.test.js @@ -35,6 +35,38 @@ describe('FormBuilder stage names translated', () => { expect(fbWrap.find('.readonly-wrap label').text()).toBe(config.i18n.override['en-US'].readOnly) }) + test('can change label of built-in control via replaceFields GH-1578', async () => { + const config = { + replaceFields: [{ + label: ['myTranslateLabel'], + type: 'text' + }], + i18n: { + location: LANGUAGE_LOCATION, + override: { + 'en-US': { + myTranslateLabel: 'Translated Field', + } + } + } + } + + const fbWrap = $('
') + const fb = await $(fbWrap).formBuilder(config).promise + + const overrideField = fbWrap.find('.cb-wrap .input-control[data-type*="text-"]') + + expect(overrideField.find('span').text()).toBe(config.i18n.override['en-US'].myTranslateLabel) + + //@TODO support adding custom fields via addField + //const field = { + // type: overrideField.data('type'), + // class: 'form-control' + //} + //fb.actions.addField(field) + //expect(fbWrap.find('.fld-label').val()).toBe(config.i18n.override['en-US'].myTranslateLabel) + }) + test('can set form to another language than default with config', async () => { const config = { i18n: {