diff --git a/src/WebformBuilder.unit.js b/src/WebformBuilder.unit.js index 5b3979ac16..33caeee583 100644 --- a/src/WebformBuilder.unit.js +++ b/src/WebformBuilder.unit.js @@ -2,6 +2,7 @@ import assert from 'power-assert'; import Harness from '../test/harness'; import WebformBuilder from './WebformBuilder'; import Builders from './builders'; +import { Formio } from './Formio'; import { uniqueApiKeys, uniqueApiKeysLayout, uniqueApiKeysSameLevel, columnsForm, resourceKeyCamelCase } from '../test/formtest'; import sameApiKeysLayoutComps from '../test/forms/sameApiKeysLayoutComps'; import testApiKeysUniquifying from '../test/forms/testApiKeysUniquifying'; @@ -269,3 +270,122 @@ describe('WebformBuilder tests', function() { }).catch(done); }); }); + +describe('Select Component selectData property', () => { + const originalMakeRequest = Formio.makeRequest; + + before((done) => { + Formio.makeRequest = () => { + return new Promise(resolve => { + const values = [{ + label: 'Label 1', + value: 'value1', + }, { + label: 'Label 2', + value: 'value2', + }]; + + resolve(values); + }); + }; + Harness.builderBefore(done); + }); + afterEach(() => Harness.getBuilder().setForm({ display: 'form', components: [] })); + + it('Should calculate selectData property for url dataSource', (done) => { + const builder = Harness.getBuilder(); + builder.setForm({}).then(() => { + Harness.buildComponent('select'); + + setTimeout(() => { + const dataSrc = builder.editForm.getComponent('dataSrc'); + dataSrc.setValue('url'); + const url = builder.editForm.getComponent(['data.url']); + const valueProperty = builder.editForm.getComponent('valueProperty'); + url.setValue('htts//fakeurl.com'); + valueProperty.setValue('value'); + + setTimeout(() => { + const defaultValue = builder.editForm.getComponent('defaultValue'); + defaultValue.setValue('value1'); + defaultValue.updateItems(null, true); + + setTimeout(() => { + assert.deepEqual(builder.editForm.data.selectData, { + label: 'Label 1', + }); + Harness.saveComponent(); + setTimeout(() => { + done(); + }, 150); + }, 250); + }, 250); + }, 150); + }).catch(done); + }); + + it('Should calculate selectData property for resource dataSource', (done) => { + const builder = Harness.getBuilder(); + builder.setForm({}).then(() => { + Harness.buildComponent('select'); + + setTimeout(() => { + const dataSrc = builder.editForm.getComponent('dataSrc'); + dataSrc.setValue('resource'); + const resource = builder.editForm.getComponent(['data.resource']); + const valueProperty = builder.editForm.getComponent('valueProperty'); + resource.setValue('12345678'); + valueProperty.setValue('value'); + + setTimeout(() => { + const defaultValue = builder.editForm.getComponent('defaultValue'); + defaultValue.setValue('value1'); + defaultValue.updateItems(null, true); + + setTimeout(() => { + assert.deepEqual(builder.editForm.data.selectData, { + label: 'Label 1', + }); + Harness.saveComponent(); + setTimeout(() => { + done(); + }, 150); + }, 250); + }, 250); + }, 150); + }).catch(done); + }); + + it('Should not calculate selectData property without valueProperty', (done) => { + const builder = Harness.getBuilder(); + builder.setForm({}).then(() => { + Harness.buildComponent('select'); + + setTimeout(() => { + const dataSrc = builder.editForm.getComponent('dataSrc'); + dataSrc.setValue('url'); + const url = builder.editForm.getComponent(['data.url']); + url.setValue('https://fakeurl.com'); + + setTimeout(() => { + const defaultValue = builder.editForm.getComponent('defaultValue'); + defaultValue.setValue('value1'); + defaultValue.updateItems(null, true); + + setTimeout(() => { + assert.equal(builder.editForm.data.selectData, undefined); + Harness.saveComponent(); + setTimeout(() => { + done(); + }, 150); + }, 250); + }, 250); + }, 150); + }).catch(done); + }); + + after((done) => { + Formio.makeRequest = originalMakeRequest; + Harness.builderAfter(done); + }); +}); diff --git a/src/components/select/Select.js b/src/components/select/Select.js index 4fc820f0bc..19fe7539a5 100644 --- a/src/components/select/Select.js +++ b/src/components/select/Select.js @@ -254,6 +254,10 @@ export default class SelectComponent extends ListComponent { return super.shouldLoad; } + get selectData() { + return this.component.selectData || super.selectData; + } + isEntireObjectDisplay() { return this.component.dataSrc === 'resource' && this.valueProperty === 'data'; } diff --git a/src/components/select/Select.unit.js b/src/components/select/Select.unit.js index 59fdbefad8..30b1bc906a 100644 --- a/src/components/select/Select.unit.js +++ b/src/components/select/Select.unit.js @@ -1212,3 +1212,4 @@ describe('Select Component with Entire Object Value Property', () => { }); }); }); + diff --git a/src/components/select/editForm/Select.edit.data.js b/src/components/select/editForm/Select.edit.data.js index df6ad3e1df..e860bf6d46 100644 --- a/src/components/select/editForm/Select.edit.data.js +++ b/src/components/select/editForm/Select.edit.data.js @@ -1,5 +1,33 @@ +import _ from 'lodash'; import { eachComponent } from '../../../utils/utils'; +const calculateSelectData = (context) => { + const { instance, data } = context; + const rawDefaultValue = instance.downloadedResources.find(resource => _.get(resource, data.valueProperty) === instance.getValue()); + const options = { data: {}, noeval: true }; + instance.interpolate(data.template, { + item: rawDefaultValue, + }, options); + return options.data.item; +}; + +const setSelectData = (context) => { + // Wait before downloadedResources will be set + setTimeout(() => { + const { instance, data } = context; + const selectDataComponent = instance?.root.getComponent('selectData'); + // nothing can set if don't have downloaded resources + if (!selectDataComponent || !instance.getValue() || !instance.downloadedResources?.length) { + return; + } + // if valueProperty is not provided, we have entire object + const shouldCalculateUrlData = data.dataSrc === 'url' && data.data.url && data.valueProperty; + const shouldCalculateResourceData = data.dataSrc === 'resource' && data.data.resource && data.valueProperty; + const newValue = shouldCalculateUrlData || shouldCalculateResourceData ? calculateSelectData(context) : undefined; + selectDataComponent.setValue(newValue); + }, 0); +}; + export default [ { key: 'dataSrc', @@ -625,5 +653,37 @@ export default [ key: 'useExactSearch', label: 'Use exact search', tooltip: 'Disables search algorithm threshold.', - } + }, + { + key: 'defaultValue', + onSetItems(component) { + setSelectData(component.evalContext()); + }, + onChange(context) { + if (context && context.flags && context.flags.modified) { + setSelectData(context); + } + }, + }, + { + key: 'selectData', + conditional: { + json: { 'and': [ + { '!==': [{ var: 'data.valueProperty' }, null] }, + { '!==': [{ var: 'data.valueProperty' }, ''] }, + ] }, + }, + }, + { + key: 'template', + onChange(context) { + if (context && context.flags && context.flags.modified) { + const defaultValueComponent = context.instance.root.getComponent('defaultValue'); + if (!defaultValueComponent) { + return; + } + setSelectData(defaultValueComponent.evalContext()); + } + }, + }, ];