Add select custom field
[MAILPOET-2453]
This commit is contained in:
committed by
Rostislav Wolný
parent
81c75fafc2
commit
ea11fdf5fb
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
.mailpoet_custom_select {
|
||||||
|
.components-base-control__label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
@@ -3,7 +3,7 @@
|
|||||||
@import '../../../node_modules/@wordpress/components/build-style/style';
|
@import '../../../node_modules/@wordpress/components/build-style/style';
|
||||||
@import '../../../node_modules/@wordpress/block-editor/build-style/style';
|
@import '../../../node_modules/@wordpress/block-editor/build-style/style';
|
||||||
@import '../../../node_modules/@wordpress/block-library/build-style/editor';
|
@import '../../../node_modules/@wordpress/block-library/build-style/editor';
|
||||||
|
|
||||||
@import './components/formEditor/components/form_title';
|
@import './components/formEditor/components/form_title';
|
||||||
@import './components/formEditor/components/sidebar';
|
@import './components/formEditor/components/sidebar';
|
||||||
@import './components/formEditor/components/block_editor';
|
@import './components/formEditor/components/block_editor';
|
||||||
|
@import './components/formEditor/components/custom_field';
|
||||||
|
@@ -15,6 +15,7 @@ import * as customText from './custom_text/custom_text.jsx';
|
|||||||
import * as customTextArea from './custom_textarea/custom_textarea.jsx';
|
import * as customTextArea from './custom_textarea/custom_textarea.jsx';
|
||||||
import * as customRadio from './custom_radio/custom_radio.jsx';
|
import * as customRadio from './custom_radio/custom_radio.jsx';
|
||||||
import * as customCheckbox from './custom_checkbox/custom_checkbox.jsx';
|
import * as customCheckbox from './custom_checkbox/custom_checkbox.jsx';
|
||||||
|
import * as customSelect from './custom_select/custom_select.jsx';
|
||||||
|
|
||||||
const registerCustomFieldBlock = (customField) => {
|
const registerCustomFieldBlock = (customField) => {
|
||||||
const namesMap = {
|
const namesMap = {
|
||||||
@@ -34,6 +35,10 @@ const registerCustomFieldBlock = (customField) => {
|
|||||||
name: customCheckbox.name,
|
name: customCheckbox.name,
|
||||||
settings: customCheckbox.getSettings(customField),
|
settings: customCheckbox.getSettings(customField),
|
||||||
},
|
},
|
||||||
|
select: {
|
||||||
|
name: customSelect.name,
|
||||||
|
settings: customSelect.getSettings(customField),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!namesMap[customField.type]) return;
|
if (!namesMap[customField.type]) return;
|
||||||
|
@@ -16,6 +16,7 @@ const PreviewItem = ({
|
|||||||
moveItem,
|
moveItem,
|
||||||
remove,
|
remove,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
|
onCheck,
|
||||||
dragFinished,
|
dragFinished,
|
||||||
}) => {
|
}) => {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
@@ -80,7 +81,7 @@ const PreviewItem = ({
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
disabled
|
onChange={(event) => onCheck(value.id, event.target.value)}
|
||||||
key={`check-${value.id}`}
|
key={`check-${value.id}`}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
@@ -104,6 +105,7 @@ PreviewItem.propTypes = {
|
|||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
onUpdate: PropTypes.func.isRequired,
|
onUpdate: PropTypes.func.isRequired,
|
||||||
|
onCheck: PropTypes.func.isRequired,
|
||||||
moveItem: PropTypes.func.isRequired,
|
moveItem: PropTypes.func.isRequired,
|
||||||
remove: PropTypes.func.isRequired,
|
remove: PropTypes.func.isRequired,
|
||||||
index: PropTypes.number.isRequired,
|
index: PropTypes.number.isRequired,
|
||||||
@@ -142,6 +144,12 @@ const Preview = ({
|
|||||||
update(value);
|
update(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onCheck = (valueId, checked) => {
|
||||||
|
const value = valuesWhileMoved.find((v) => v.id === valueId);
|
||||||
|
value.isChecked = checked;
|
||||||
|
update(value);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DndProvider backend={Backend}>
|
<DndProvider backend={Backend}>
|
||||||
{valuesWhileMoved.map((value, index) => (
|
{valuesWhileMoved.map((value, index) => (
|
||||||
@@ -151,6 +159,7 @@ const Preview = ({
|
|||||||
value={value}
|
value={value}
|
||||||
moveItem={moveItem}
|
moveItem={moveItem}
|
||||||
remove={remove}
|
remove={remove}
|
||||||
|
onCheck={onCheck}
|
||||||
onUpdate={onUpdate}
|
onUpdate={onUpdate}
|
||||||
dragFinished={() => onReorder(valuesWhileMoved)}
|
dragFinished={() => onReorder(valuesWhileMoved)}
|
||||||
/>
|
/>
|
||||||
|
@@ -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;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
115
assets/js/src/form_editor/blocks/custom_select/edit.jsx
Normal file
115
assets/js/src/form_editor/blocks/custom_select/edit.jsx
Normal file
@@ -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 = (
|
||||||
|
<InspectorControls>
|
||||||
|
<Panel>
|
||||||
|
<PanelBody title={MailPoet.I18n.t('customFieldSettings')} initialOpen>
|
||||||
|
<CustomFieldSettings
|
||||||
|
mandatory={attributes.mandatory}
|
||||||
|
values={attributes.values}
|
||||||
|
isSaving={isSaving}
|
||||||
|
onSave={(params) => 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,
|
||||||
|
}),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</PanelBody>
|
||||||
|
</Panel>
|
||||||
|
<Panel>
|
||||||
|
<PanelBody title={MailPoet.I18n.t('formSettings')} initialOpen>
|
||||||
|
<TextControl
|
||||||
|
label={MailPoet.I18n.t('label')}
|
||||||
|
value={attributes.label}
|
||||||
|
data-automation-id="settings_custom_text_label_input"
|
||||||
|
onChange={(label) => (setAttributes({ label }))}
|
||||||
|
/>
|
||||||
|
<ToggleControl
|
||||||
|
label={MailPoet.I18n.t('displayLabelWithinInput')}
|
||||||
|
checked={attributes.labelWithinInput}
|
||||||
|
onChange={(labelWithinInput) => (setAttributes({ labelWithinInput }))}
|
||||||
|
/>
|
||||||
|
</PanelBody>
|
||||||
|
</Panel>
|
||||||
|
</InspectorControls>
|
||||||
|
);
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div className="mailpoet_custom_select">
|
||||||
|
<SelectControl
|
||||||
|
label={!attributes.labelWithinInput ? getLabel() : undefined}
|
||||||
|
options={options}
|
||||||
|
onChange={() => {}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
@@ -20,6 +20,9 @@ const mapCustomField = (block, customFields, mappedCommonProperties) => {
|
|||||||
if (block.name.startsWith('mailpoet-form/custom-checkbox')) {
|
if (block.name.startsWith('mailpoet-form/custom-checkbox')) {
|
||||||
mapped.type = 'checkbox';
|
mapped.type = 'checkbox';
|
||||||
}
|
}
|
||||||
|
if (block.name.startsWith('mailpoet-form/custom-select')) {
|
||||||
|
mapped.type = 'select';
|
||||||
|
}
|
||||||
if (has(block.attributes, 'validate')) {
|
if (has(block.attributes, 'validate')) {
|
||||||
mapped.params.validate = block.attributes.validate;
|
mapped.params.validate = block.attributes.validate;
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ const mapCustomField = (item, customFields, mappedCommonProperties) => {
|
|||||||
textarea: 'mailpoet-form/custom-textarea',
|
textarea: 'mailpoet-form/custom-textarea',
|
||||||
radio: 'mailpoet-form/custom-radio',
|
radio: 'mailpoet-form/custom-radio',
|
||||||
checkbox: 'mailpoet-form/custom-checkbox',
|
checkbox: 'mailpoet-form/custom-checkbox',
|
||||||
|
select: 'mailpoet-form/custom-select',
|
||||||
};
|
};
|
||||||
const mapped = {
|
const mapped = {
|
||||||
...mappedCommonProperties,
|
...mappedCommonProperties,
|
||||||
|
@@ -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 = {
|
const dividerBlock = {
|
||||||
clientId: 'some_random_123',
|
clientId: 'some_random_123',
|
||||||
@@ -297,6 +313,33 @@ describe('Blocks to Form Body', () => {
|
|||||||
expect(input.params.validate).to.eq('alphanum');
|
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', () => {
|
it('Should map custom radio field', () => {
|
||||||
const customField = {
|
const customField = {
|
||||||
created_at: '2019-12-10T15:05:06+00:00',
|
created_at: '2019-12-10T15:05:06+00:00',
|
||||||
|
@@ -103,6 +103,24 @@ const customRadioInput = {
|
|||||||
},
|
},
|
||||||
position: null,
|
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 = {
|
const customCheckboxInput = {
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
name: 'Custom check',
|
name: 'Custom check',
|
||||||
@@ -365,6 +383,33 @@ describe('Form Body To Blocks', () => {
|
|||||||
expect(block.attributes.values[0]).to.have.property('isChecked', true);
|
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', () => {
|
it('Should ignore unknown input type', () => {
|
||||||
const blocks = formBodyToBlocks([{ ...submitInput, id: 'some-nonsense' }]);
|
const blocks = formBodyToBlocks([{ ...submitInput, id: 'some-nonsense' }]);
|
||||||
expect(blocks).to.be.empty;
|
expect(blocks).to.be.empty;
|
||||||
|
Reference in New Issue
Block a user