diff --git a/assets/css/src/components/formEditor/components/_custom_field.scss b/assets/css/src/components/formEditor/components/_custom_field.scss new file mode 100644 index 0000000000..abe09705df --- /dev/null +++ b/assets/css/src/components/formEditor/components/_custom_field.scss @@ -0,0 +1,6 @@ + +.mailpoet_custom_select { + .components-base-control__label { + display: block; + } +} diff --git a/assets/css/src/formEditor.scss b/assets/css/src/formEditor.scss index 5111e54312..338f174075 100644 --- a/assets/css/src/formEditor.scss +++ b/assets/css/src/formEditor.scss @@ -3,7 +3,7 @@ @import '../../../node_modules/@wordpress/components/build-style/style'; @import '../../../node_modules/@wordpress/block-editor/build-style/style'; @import '../../../node_modules/@wordpress/block-library/build-style/editor'; - @import './components/formEditor/components/form_title'; @import './components/formEditor/components/sidebar'; @import './components/formEditor/components/block_editor'; +@import './components/formEditor/components/custom_field'; diff --git a/assets/js/src/form_editor/blocks/blocks.jsx b/assets/js/src/form_editor/blocks/blocks.jsx index a9a02fada7..1c85a8768b 100644 --- a/assets/js/src/form_editor/blocks/blocks.jsx +++ b/assets/js/src/form_editor/blocks/blocks.jsx @@ -15,6 +15,7 @@ import * as customText from './custom_text/custom_text.jsx'; import * as customTextArea from './custom_textarea/custom_textarea.jsx'; import * as customRadio from './custom_radio/custom_radio.jsx'; import * as customCheckbox from './custom_checkbox/custom_checkbox.jsx'; +import * as customSelect from './custom_select/custom_select.jsx'; const registerCustomFieldBlock = (customField) => { const namesMap = { @@ -34,6 +35,10 @@ const registerCustomFieldBlock = (customField) => { name: customCheckbox.name, settings: customCheckbox.getSettings(customField), }, + select: { + name: customSelect.name, + settings: customSelect.getSettings(customField), + }, }; if (!namesMap[customField.type]) return; diff --git a/assets/js/src/form_editor/blocks/custom_radio/settings_preview.jsx b/assets/js/src/form_editor/blocks/custom_radio/settings_preview.jsx index 619e253df7..fbff5735aa 100644 --- a/assets/js/src/form_editor/blocks/custom_radio/settings_preview.jsx +++ b/assets/js/src/form_editor/blocks/custom_radio/settings_preview.jsx @@ -16,6 +16,7 @@ const PreviewItem = ({ moveItem, remove, onUpdate, + onCheck, dragFinished, }) => { const ref = useRef(null); @@ -80,7 +81,7 @@ const PreviewItem = ({ > onCheck(value.id, event.target.value)} key={`check-${value.id}`} /> { + const value = valuesWhileMoved.find((v) => v.id === valueId); + value.isChecked = checked; + update(value); + }; + return ( {valuesWhileMoved.map((value, index) => ( @@ -151,6 +159,7 @@ const Preview = ({ value={value} moveItem={moveItem} remove={remove} + onCheck={onCheck} onUpdate={onUpdate} dragFinished={() => onReorder(valuesWhileMoved)} /> diff --git a/assets/js/src/form_editor/blocks/custom_select/custom_select.jsx b/assets/js/src/form_editor/blocks/custom_select/custom_select.jsx new file mode 100644 index 0000000000..7e9a666982 --- /dev/null +++ b/assets/js/src/form_editor/blocks/custom_select/custom_select.jsx @@ -0,0 +1,44 @@ +import Icon from '../custom_text/icon.jsx'; +import Edit from './edit.jsx'; + +export const name = 'mailpoet-form/custom-select'; + +export function getSettings(customField) { + return { + title: customField.name, + description: '', + icon: Icon, + category: 'custom-fields', + attributes: { + label: { + type: 'string', + default: customField.name, + }, + labelWithinInput: { + type: 'boolean', + default: true, + }, + mandatory: { + type: 'boolean', + default: false, + }, + values: { + type: 'array', + default: [], + }, + customFieldId: { + type: 'string', + default: customField.id, + }, + }, + supports: { + html: false, + customClassName: false, + multiple: false, + }, + edit: Edit, + save() { + return null; + }, + }; +} diff --git a/assets/js/src/form_editor/blocks/custom_select/edit.jsx b/assets/js/src/form_editor/blocks/custom_select/edit.jsx new file mode 100644 index 0000000000..755d54c7ca --- /dev/null +++ b/assets/js/src/form_editor/blocks/custom_select/edit.jsx @@ -0,0 +1,115 @@ +import React from 'react'; +import { + Panel, + PanelBody, + SelectControl, + TextControl, + ToggleControl, +} from '@wordpress/components'; +import { InspectorControls } from '@wordpress/block-editor'; +import PropTypes from 'prop-types'; +import MailPoet from 'mailpoet'; +import { useDispatch, useSelect } from '@wordpress/data'; + +import CustomFieldSettings from '../custom_radio/custom_field_settings.jsx'; + +const CustomSelectEdit = ({ attributes, setAttributes }) => { + const isSaving = useSelect( + (sel) => sel('mailpoet-form-editor').getIsCustomFieldSaving(), + [] + ); + const { saveCustomField } = useDispatch('mailpoet-form-editor'); + + const inspectorControls = ( + + + + saveCustomField({ + customFieldId: attributes.customFieldId, + data: { + params: { + required: params.mandatory ? '1' : undefined, + values: params.values.map((value) => ({ + value: value.name, + is_checked: value.isChecked ? '1' : undefined, + })), + }, + }, + onFinish: () => setAttributes({ + mandatory: params.mandatory, + values: params.values, + }), + })} + /> + + + + + (setAttributes({ label }))} + /> + (setAttributes({ labelWithinInput }))} + /> + + + + ); + + const getLabel = () => { + if (attributes.mandatory) { + return `${attributes.label} *`; + } + return attributes.label; + }; + + const getInput = () => { + const options = [{ + label: attributes.labelWithinInput ? getLabel() : '-', + value: null, + }]; + if (Array.isArray(attributes.values) || !attributes.values.length) { + attributes.values.forEach((value) => options.push({ label: value.name })); + } + return ( +
+ {}} + /> +
+ ); + }; + + return ( + <> + {inspectorControls} + {getInput()} + + ); +}; + +CustomSelectEdit.propTypes = { + attributes: PropTypes.shape({ + label: PropTypes.string.isRequired, + values: PropTypes.arrayOf(PropTypes.shape({ + name: PropTypes.string.isRequired, + isChecked: PropTypes.bool, + id: PropTypes.string.isRequired, + })), + mandatory: PropTypes.bool.isRequired, + }).isRequired, + setAttributes: PropTypes.func.isRequired, +}; + +export default CustomSelectEdit; diff --git a/assets/js/src/form_editor/store/blocks_to_form_body.jsx b/assets/js/src/form_editor/store/blocks_to_form_body.jsx index 0c3257f6eb..ea720f4e51 100644 --- a/assets/js/src/form_editor/store/blocks_to_form_body.jsx +++ b/assets/js/src/form_editor/store/blocks_to_form_body.jsx @@ -20,6 +20,9 @@ const mapCustomField = (block, customFields, mappedCommonProperties) => { if (block.name.startsWith('mailpoet-form/custom-checkbox')) { mapped.type = 'checkbox'; } + if (block.name.startsWith('mailpoet-form/custom-select')) { + mapped.type = 'select'; + } if (has(block.attributes, 'validate')) { mapped.params.validate = block.attributes.validate; } diff --git a/assets/js/src/form_editor/store/form_body_to_blocks.jsx b/assets/js/src/form_editor/store/form_body_to_blocks.jsx index be459bc354..d272999473 100644 --- a/assets/js/src/form_editor/store/form_body_to_blocks.jsx +++ b/assets/js/src/form_editor/store/form_body_to_blocks.jsx @@ -10,6 +10,7 @@ const mapCustomField = (item, customFields, mappedCommonProperties) => { textarea: 'mailpoet-form/custom-textarea', radio: 'mailpoet-form/custom-radio', checkbox: 'mailpoet-form/custom-checkbox', + select: 'mailpoet-form/custom-select', }; const mapped = { ...mappedCommonProperties, diff --git a/tests/javascript/form_editor/store/blocks_to_form_body.spec.js b/tests/javascript/form_editor/store/blocks_to_form_body.spec.js index 33cea6b34b..d74cc41179 100644 --- a/tests/javascript/form_editor/store/blocks_to_form_body.spec.js +++ b/tests/javascript/form_editor/store/blocks_to_form_body.spec.js @@ -107,6 +107,22 @@ const customCheckBox = { }, }; +const customSelectBlock = { + clientId: '5', + isValid: true, + innerBlocks: [], + name: 'mailpoet-form/custom-select', + attributes: { + label: 'Select', + labelWithinInput: false, + mandatory: false, + customFieldId: 6, + values: [ + { name: 'option 1' }, + { name: 'option 2' }, + ], + }, +}; const dividerBlock = { clientId: 'some_random_123', @@ -297,6 +313,33 @@ describe('Blocks to Form Body', () => { expect(input.params.validate).to.eq('alphanum'); }); + it('Should map custom select field', () => { + const customField = { + created_at: '2019-12-10T15:05:06+00:00', + id: 6, + name: 'Custom Select', + params: { + label: 'Select', + required: '1', + values: [ + { value: 'option 1' }, + ], + }, + type: 'select', + updated_at: '2019-12-10T15:05:06+00:00', + }; + const [input] = formBlocksToBody([customSelectBlock], [customField]); + checkBodyInputBasics(input); + expect(input.id).to.be.equal('6'); + expect(input.name).to.be.equal('Custom Select'); + expect(input.type).to.be.equal('select'); + expect(input.position).to.be.equal('1'); + expect(input.params.label).to.be.equal('Select'); + expect(input.params.values).to.be.an('Array').that.has.length(2); + expect(input.params.values[0]).to.have.property('value', 'option 1'); + expect(input.params.values[1]).to.have.property('value', 'option 2'); + }); + it('Should map custom radio field', () => { const customField = { created_at: '2019-12-10T15:05:06+00:00', diff --git a/tests/javascript/form_editor/store/form_body_to_blocks.spec.js b/tests/javascript/form_editor/store/form_body_to_blocks.spec.js index 69367889e9..e0e1da646c 100644 --- a/tests/javascript/form_editor/store/form_body_to_blocks.spec.js +++ b/tests/javascript/form_editor/store/form_body_to_blocks.spec.js @@ -103,6 +103,24 @@ const customRadioInput = { }, position: null, }; +const customSelectInput = { + type: 'select', + name: 'Custom select', + id: '5', + unique: '1', + static: '0', + params: { + required: '', + label: 'Select', + label_within: '1', + values: [ + { + value: 'option 1', + }, + ], + }, + position: null, +}; const customCheckboxInput = { type: 'checkbox', name: 'Custom check', @@ -365,6 +383,33 @@ describe('Form Body To Blocks', () => { expect(block.attributes.values[0]).to.have.property('isChecked', true); }); + it('Should map custom select input to block', () => { + const customField = { + type: 'select', + name: 'Custom select', + id: 5, + params: { + required: '', + label: 'Select', + values: [ + { + value: 'option 1', + }, + ], + }, + position: null, + }; + const [block] = formBodyToBlocks([{ ...customSelectInput, position: '1' }], [customField]); + checkBlockBasics(block); + expect(block.clientId).to.be.equal('5_0'); + expect(block.name).to.be.equal('mailpoet-form/custom-select-customselect'); + expect(block.attributes.label).to.be.equal('Select'); + expect(block.attributes.mandatory).to.be.equal(false); + expect(block.attributes.labelWithinInput).to.be.equal(true); + expect(block.attributes.values).to.be.an('Array').that.has.length(1); + expect(block.attributes.values[0]).to.have.property('name', 'option 1'); + }); + it('Should ignore unknown input type', () => { const blocks = formBodyToBlocks([{ ...submitInput, id: 'some-nonsense' }]); expect(blocks).to.be.empty;