Add select custom field

[MAILPOET-2453]
This commit is contained in:
Pavel Dohnal
2019-12-18 12:11:13 +01:00
committed by Rostislav Wolný
parent 81c75fafc2
commit ea11fdf5fb
10 changed files with 273 additions and 2 deletions

View File

@@ -0,0 +1,6 @@
.mailpoet_custom_select {
.components-base-control__label {
display: block;
}
}

View File

@@ -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';

View File

@@ -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;

View File

@@ -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)}
/> />

View File

@@ -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;
},
};
}

View 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;

View File

@@ -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;
} }

View File

@@ -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,

View File

@@ -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',

View File

@@ -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;