From 461f1d85061d510de81310426537d5fa93ae2ec0 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Tue, 22 Nov 2022 22:35:14 +0000 Subject: [PATCH 01/51] Add UI option to 'attach XML' --- src/formSchema.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/formSchema.js b/src/formSchema.js index e351aad..ccbab7f 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -1,5 +1,4 @@ -import { defineMessages } from 'react-intl'; -import { useIntl } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; const messages = defineMessages({ form: { @@ -47,9 +46,13 @@ const messages = defineMessages({ id: 'form_send_email', defaultMessage: 'Send email to recipient', }, + attachXml: { + id: 'form_attach_xml', + defaultMessage: 'Attach XML to email', + }, }); -export default () => { +export default (formData) => { var intl = useIntl(); return { @@ -68,6 +71,7 @@ export default () => { 'captcha', 'store', 'send', + ...[formData.send ? 'attachXml' : null], ], }, ], @@ -107,6 +111,10 @@ export default () => { type: 'boolean', title: intl.formatMessage(messages.send), }, + attachXml: { + type: 'boolean', + title: intl.formatMessage(messages.attachXml), + }, }, required: ['default_to', 'default_from', 'default_subject'], }; From 17cb7857c528eaf0ec3b2dcada98132ac77cc7a2 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Tue, 22 Nov 2022 23:20:53 +0000 Subject: [PATCH 02/51] Move the 'send XML' option into a fieldset --- src/formSchema.js | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/formSchema.js b/src/formSchema.js index ccbab7f..77af064 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -55,26 +55,35 @@ const messages = defineMessages({ export default (formData) => { var intl = useIntl(); + const fieldsets = [ + { + id: 'default', + title: 'Default', + fields: [ + 'title', + 'description', + 'default_to', + 'default_from', + 'default_subject', + 'submit_label', + 'captcha', + 'store', + 'send', + ], + }, + ]; + + if (formData.send) { + fieldsets.push({ + id: 'sendingOptions', + title: 'Sending options', + fields: ['attachXml'], + }); + } + return { title: intl.formatMessage(messages.form), - fieldsets: [ - { - id: 'default', - title: 'Default', - fields: [ - 'title', - 'description', - 'default_to', - 'default_from', - 'default_subject', - 'submit_label', - 'captcha', - 'store', - 'send', - ...[formData.send ? 'attachXml' : null], - ], - }, - ], + fieldsets: fieldsets, properties: { title: { title: intl.formatMessage(messages.title), From 8e4ba085d6b73a90be23e1facb34083203896616 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 23 Nov 2022 16:07:29 +0000 Subject: [PATCH 03/51] Add UI for adding custom IDs for fields --- src/formSchema.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/formSchema.js b/src/formSchema.js index 77af064..47c4474 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -50,6 +50,10 @@ const messages = defineMessages({ id: 'form_attach_xml', defaultMessage: 'Attach XML to email', }, + storedDataIds: { + id: 'form_stored_data_ids', + defaultMessage: 'Data ID mapping', + }, }); export default (formData) => { @@ -81,6 +85,14 @@ export default (formData) => { }); } + if (formData.send || formData.store) { + fieldsets.push({ + id: 'sendingOptions', + title: intl.formatMessage(messages.storedDataIds), + fields: formData.subblocks.map((subblock) => `mapping-${subblock.label}`), + }); + } + return { title: intl.formatMessage(messages.form), fieldsets: fieldsets, @@ -124,6 +136,12 @@ export default (formData) => { type: 'boolean', title: intl.formatMessage(messages.attachXml), }, + ...Object.assign( + {}, + ...formData.subblocks.map((subblock) => { + return { [`mapping-${subblock.label}`]: { title: subblock.label } }; + }), + ), }, required: ['default_to', 'default_from', 'default_subject'], }; From 76b64f88eae84baaede98d91df4796d59b8f1cca Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 23 Nov 2022 16:34:23 +0000 Subject: [PATCH 04/51] Use internal field_id for mapping field names --- src/formSchema.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/formSchema.js b/src/formSchema.js index 47c4474..a4cad89 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -89,7 +89,7 @@ export default (formData) => { fieldsets.push({ id: 'sendingOptions', title: intl.formatMessage(messages.storedDataIds), - fields: formData.subblocks.map((subblock) => `mapping-${subblock.label}`), + fields: formData.subblocks.map((subblock) => subblock.field_id), }); } @@ -139,7 +139,7 @@ export default (formData) => { ...Object.assign( {}, ...formData.subblocks.map((subblock) => { - return { [`mapping-${subblock.label}`]: { title: subblock.label } }; + return { [subblock.field_id]: { title: subblock.label } }; }), ), }, From 84f5f0183daf1d2e6d7b07471c44caa210ea2a75 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 23 Nov 2022 16:40:46 +0000 Subject: [PATCH 05/51] Send the custom_field_id to the backend --- src/components/View.jsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/View.jsx b/src/components/View.jsx index 393fcc9..d3da01f 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -81,7 +81,15 @@ const View = ({ data, id, path }) => { const captchaToken = useRef(); const onChangeFormData = (field_id, field, value, extras) => { - setFormData({ field, value: { field_id, value, ...extras } }); + setFormData({ + field, + value: { + field_id, + value, + ...(data[field_id] && { custom_field_id: data[field_id] }), // Conditionally add the key. Nicer to work with than having a key with a null value + ...extras, + }, + }); }; useEffect(() => { From 2873f8cd3ecf670aa5c6e811f1c82a6c4a472ee0 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 23 Nov 2022 19:00:04 +0000 Subject: [PATCH 06/51] Fix dataIdMapping accordion key --- src/formSchema.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/formSchema.js b/src/formSchema.js index a4cad89..54786be 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -87,7 +87,7 @@ export default (formData) => { if (formData.send || formData.store) { fieldsets.push({ - id: 'sendingOptions', + id: 'storedDataIds', title: intl.formatMessage(messages.storedDataIds), fields: formData.subblocks.map((subblock) => subblock.field_id), }); From c0a3740b64a44b43e588b41ef2e3efa3f1347c89 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Mon, 28 Nov 2022 23:37:44 +0000 Subject: [PATCH 07/51] Hidden field --- src/components/Field.jsx | 4 +++ .../HiddenSchemaExtender.js | 20 +++++++++++ .../FieldTypeSchemaExtenders/index.js | 1 + src/components/Widget/HiddenWidget.jsx | 33 +++++++++++++++++++ src/components/Widget/index.js | 1 + src/fieldSchema.js | 5 +++ src/index.js | 2 ++ 7 files changed, 66 insertions(+) create mode 100644 src/components/FieldTypeSchemaExtenders/HiddenSchemaExtender.js create mode 100644 src/components/Widget/HiddenWidget.jsx diff --git a/src/components/Field.jsx b/src/components/Field.jsx index a5a022b..8e52cc9 100644 --- a/src/components/Field.jsx +++ b/src/components/Field.jsx @@ -12,6 +12,7 @@ import { TextareaWidget, CheckboxListWidget, RadioWidget, + HiddenWidget, } from 'volto-form-block/components/Widget'; import config from '@plone/volto/registry'; @@ -195,6 +196,9 @@ const Field = ({ {...(isInvalid() ? { className: 'is-invalid' } : {})} /> )} + {field_type === 'hidden' && ( + + )} {field_type === 'static_text' && (isOnEdit ? ( { + return { + fields: ['input_value'], + properties: { + input_value: { + title: intl.formatMessage(messages.field_input_value), + type: 'text', + }, + }, + required: ['input_value'], + }; +}; diff --git a/src/components/FieldTypeSchemaExtenders/index.js b/src/components/FieldTypeSchemaExtenders/index.js index fb9659a..81e0302 100644 --- a/src/components/FieldTypeSchemaExtenders/index.js +++ b/src/components/FieldTypeSchemaExtenders/index.js @@ -1,2 +1,3 @@ export { SelectionSchemaExtender } from './SelectionSchemaExtender'; export { FromSchemaExtender } from './FromSchemaExtender'; +export { HiddenSchemaExtender } from './HiddenSchemaExtender'; diff --git a/src/components/Widget/HiddenWidget.jsx b/src/components/Widget/HiddenWidget.jsx new file mode 100644 index 0000000..b314854 --- /dev/null +++ b/src/components/Widget/HiddenWidget.jsx @@ -0,0 +1,33 @@ +import PropTypes from 'prop-types'; + +/** + * Displays an ``. + */ +export const HiddenWidget = ({ id, title, value, isOnEdit }) => { + const inputId = `field-${id}`; + return ( + <> + {isOnEdit ? : null} + + + ); +}; + +HiddenWidget.propTypes = { + id: PropTypes.string.isRequired, + title: PropTypes.string, + description: PropTypes.string, + required: PropTypes.bool, + error: PropTypes.arrayOf(PropTypes.string), + value: PropTypes.string, + focus: PropTypes.bool, + onChange: PropTypes.func, + onBlur: PropTypes.func, + onClick: PropTypes.func, + onEdit: PropTypes.func, + onDelete: PropTypes.func, + minLength: PropTypes.number, + maxLength: PropTypes.number, + wrapped: PropTypes.bool, + placeholder: PropTypes.string, +}; diff --git a/src/components/Widget/index.js b/src/components/Widget/index.js index d26f3ca..1b2cb56 100644 --- a/src/components/Widget/index.js +++ b/src/components/Widget/index.js @@ -6,6 +6,7 @@ export { default as EmailWidget } from 'volto-form-block/components/Widget/Email export { default as FileWidget } from 'volto-form-block/components/Widget/FileWidget'; export { default as GoogleReCaptchaWidget } from 'volto-form-block/components/Widget/GoogleReCaptchaWidget'; export { default as HCaptchaWidget } from 'volto-form-block/components/Widget/HCaptchaWidget'; +export { HiddenWidget } from 'volto-form-block/components/Widget/HiddenWidget'; export { default as HoneypotCaptchaWidget } from 'volto-form-block/components/Widget/HoneypotCaptchaWidget'; export { default as NoRobotsCaptchaWidget } from 'volto-form-block/components/Widget/NoRobotsCaptchaWidget'; export { default as RadioWidget } from 'volto-form-block/components/Widget/RadioWidget'; diff --git a/src/fieldSchema.js b/src/fieldSchema.js index 95916b5..1e9747e 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -63,6 +63,10 @@ const messages = defineMessages({ id: 'form_field_type_static_text', defaultMessage: 'Static text', }, + field_type_hidden: { + id: 'form_field_type_hidden', + defaultMessage: 'Hidden', + }, }); export default (props) => { @@ -81,6 +85,7 @@ export default (props) => { ['attachment', intl.formatMessage(messages.field_type_attachment)], ['from', intl.formatMessage(messages.field_type_from)], ['static_text', intl.formatMessage(messages.field_type_static_text)], + ['hidden', intl.formatMessage(messages.field_type_hidden)], ]; var attachmentDescription = props?.field_type === 'attachment' diff --git a/src/index.js b/src/index.js index 676ed26..6d9856c 100644 --- a/src/index.js +++ b/src/index.js @@ -18,6 +18,7 @@ import FieldSchema from 'volto-form-block/fieldSchema'; import { SelectionSchemaExtender, FromSchemaExtender, + HiddenSchemaExtender, } from './components/FieldTypeSchemaExtenders'; export { submitForm, @@ -43,6 +44,7 @@ const applyConfig = (config) => { single_choice: SelectionSchemaExtender, multiple_choice: SelectionSchemaExtender, from: FromSchemaExtender, + hidden: HiddenSchemaExtender, }, restricted: false, mostUsed: true, From 87e6814d724fc6c05892e161f1caaa98241d4482 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 30 Nov 2022 18:06:35 +0000 Subject: [PATCH 08/51] Change 'Checkbox' to 'Yes/ No' and allow multiple widgets --- src/components/Field.jsx | 61 ++++++++++++++----- .../YesNoSchemaExtender.js | 25 ++++++++ .../FieldTypeSchemaExtenders/index.js | 1 + src/fieldSchema.js | 8 +-- src/index.js | 2 + 5 files changed, 77 insertions(+), 20 deletions(-) create mode 100644 src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js diff --git a/src/components/Field.jsx b/src/components/Field.jsx index a5a022b..18233e5 100644 --- a/src/components/Field.jsx +++ b/src/components/Field.jsx @@ -25,32 +25,61 @@ const messages = defineMessages({ }, }); +const widgetMapping = { + single_choice: RadioWidget, + checkbox: CheckboxWidget, +}; + /** * Field class. * @class View * @extends Component */ -const Field = ({ - label, - description, - name, - field_type, - required, - input_values, - value, - onChange, - isOnEdit, - valid, - disabled = false, - formHasErrors = false, - id, -}) => { +const Field = (props) => { + const { + label, + description, + name, + field_type, + required, + input_values, + value, + onChange, + isOnEdit, + valid, + disabled = false, + formHasErrors = false, + id, + widget, + } = props; const intl = useIntl(); const isInvalid = () => { return !isOnEdit && !valid; }; + if (widget) { + const Widget = widgetMapping[widget]; + const valueList = + field_type === 'yes_no' + ? [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ] + : [...(input_values?.map((v) => ({ value: v, label: v })) ?? [])]; + + return ( + + ); + } + return (
{field_type === 'text' && ( @@ -135,7 +164,7 @@ const Field = ({ {...(isInvalid() ? { className: 'is-invalid' } : {})} /> )} - {field_type === 'checkbox' && ( + {(field_type === 'yes_no' || field_type === 'checkbox') && ( { + return { + fields: ['widget'], + properties: { + widget: { + title: intl.formatMessage(messages.field_widget), + type: 'array', + choices: [ + ['checkbox', 'Checkbox'], + ['single_choice', 'Radio'], + ], + default: 'checkbox', + }, + }, + required: ['widget'], + }; +}; diff --git a/src/components/FieldTypeSchemaExtenders/index.js b/src/components/FieldTypeSchemaExtenders/index.js index fb9659a..09d86b8 100644 --- a/src/components/FieldTypeSchemaExtenders/index.js +++ b/src/components/FieldTypeSchemaExtenders/index.js @@ -1,2 +1,3 @@ export { SelectionSchemaExtender } from './SelectionSchemaExtender'; export { FromSchemaExtender } from './FromSchemaExtender'; +export { YesNoSchemaExtender } from './YesNoSchemaExtender'; diff --git a/src/fieldSchema.js b/src/fieldSchema.js index 95916b5..a8e805e 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -39,9 +39,9 @@ const messages = defineMessages({ id: 'form_field_type_multiple_choice', defaultMessage: 'Multiple choice', }, - field_type_checkbox: { - id: 'form_field_type_checkbox', - defaultMessage: 'Checkbox', + field_type_yes_no: { + id: 'field_type_yes_no', + defaultMessage: 'Yes/ No', }, field_type_date: { id: 'form_field_type_date', @@ -76,7 +76,7 @@ export default (props) => { 'multiple_choice', intl.formatMessage(messages.field_type_multiple_choice), ], - ['checkbox', intl.formatMessage(messages.field_type_checkbox)], + ['yes_no', intl.formatMessage(messages.field_type_yes_no)], ['date', intl.formatMessage(messages.field_type_date)], ['attachment', intl.formatMessage(messages.field_type_attachment)], ['from', intl.formatMessage(messages.field_type_from)], diff --git a/src/index.js b/src/index.js index 676ed26..87efc51 100644 --- a/src/index.js +++ b/src/index.js @@ -18,6 +18,7 @@ import FieldSchema from 'volto-form-block/fieldSchema'; import { SelectionSchemaExtender, FromSchemaExtender, + YesNoSchemaExtender } from './components/FieldTypeSchemaExtenders'; export { submitForm, @@ -43,6 +44,7 @@ const applyConfig = (config) => { single_choice: SelectionSchemaExtender, multiple_choice: SelectionSchemaExtender, from: FromSchemaExtender, + yes_no: YesNoSchemaExtender, }, restricted: false, mostUsed: true, From a91d7752840402980c7ec8558a06f99dcbee0064 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Thu, 1 Dec 2022 12:07:28 +0000 Subject: [PATCH 09/51] Fix hidden fields not being included in form data --- src/components/View.jsx | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/components/View.jsx b/src/components/View.jsx index 393fcc9..a59b27b 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -48,12 +48,29 @@ const formStateReducer = (state, action) => { } }; -const getInitialData = (data) => ({ - ...data.reduce( - (acc, field) => ({ ...acc, [getFieldName(field.label, field.id)]: field }), - {}, - ), -}); +const getInitialData = (data) => { + const { static_fields = [], subblocks = [] } = data; + + return { + ...subblocks.reduce( + (acc, field) => + field.field_type === 'hidden' + ? { + ...acc, + [getFieldName(field.label, field.id)]: field, + } + : acc, + {}, + ), + ...static_fields.reduce( + (acc, field) => ({ + ...acc, + [getFieldName(field.label, field.id)]: field, + }), + {}, + ), + }; +}; /** * Form view @@ -62,18 +79,17 @@ const getInitialData = (data) => ({ const View = ({ data, id, path }) => { const intl = useIntl(); const dispatch = useDispatch(); - const { static_fields = [] } = data; const [formData, setFormData] = useReducer((state, action) => { if (action.reset) { - return getInitialData(static_fields); + return getInitialData(data); } return { ...state, [action.field]: action.value, }; - }, getInitialData(static_fields)); + }, getInitialData(data)); const [formState, setFormState] = useReducer(formStateReducer, initialState); const [formErrors, setFormErrors] = useState([]); From 830259690990041fb53f9bd08de0ddc214781d5f Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Thu, 1 Dec 2022 13:10:18 +0000 Subject: [PATCH 10/51] Fix hidden fields value not being correctly sent to backend --- .../FieldTypeSchemaExtenders/HiddenSchemaExtender.js | 6 +++--- src/components/View.jsx | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/FieldTypeSchemaExtenders/HiddenSchemaExtender.js b/src/components/FieldTypeSchemaExtenders/HiddenSchemaExtender.js index 03909c6..48e89c6 100644 --- a/src/components/FieldTypeSchemaExtenders/HiddenSchemaExtender.js +++ b/src/components/FieldTypeSchemaExtenders/HiddenSchemaExtender.js @@ -8,13 +8,13 @@ const messages = defineMessages({ export const HiddenSchemaExtender = (intl) => { return { - fields: ['input_value'], + fields: ['value'], properties: { - input_value: { + value: { title: intl.formatMessage(messages.field_input_value), type: 'text', }, }, - required: ['input_value'], + required: ['value'], }; }; diff --git a/src/components/View.jsx b/src/components/View.jsx index a59b27b..c9b25d4 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -57,7 +57,10 @@ const getInitialData = (data) => { field.field_type === 'hidden' ? { ...acc, - [getFieldName(field.label, field.id)]: field, + [getFieldName(field.label, field.id)]: { + ...field, + ...(data[field.id] && { custom_field_id: data[field.id] }), + }, } : acc, {}, From 1d8c867036cfd0fec5a301863642e50cc465b412 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Thu, 1 Dec 2022 14:01:39 +0000 Subject: [PATCH 11/51] Fix crash when creating new form block --- src/formSchema.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/formSchema.js b/src/formSchema.js index 54786be..b6f04c3 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -136,12 +136,14 @@ export default (formData) => { type: 'boolean', title: intl.formatMessage(messages.attachXml), }, - ...Object.assign( - {}, - ...formData.subblocks.map((subblock) => { - return { [subblock.field_id]: { title: subblock.label } }; - }), - ), + ...(formData.subblocks + ? Object.assign( + {}, + ...formData.subblocks.map((subblock) => { + return { [subblock.field_id]: { title: subblock.label } }; + }), + ) + : {}), }, required: ['default_to', 'default_from', 'default_subject'], }; From b2efeed31558f5bb776126444313eb299dd8c18d Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Thu, 1 Dec 2022 14:07:31 +0000 Subject: [PATCH 12/51] Add comment --- src/formSchema.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/formSchema.js b/src/formSchema.js index b6f04c3..fcf8d43 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -136,6 +136,7 @@ export default (formData) => { type: 'boolean', title: intl.formatMessage(messages.attachXml), }, + // Add properties for each of the fields for use in the data mapping ...(formData.subblocks ? Object.assign( {}, From e85cfe2e2b54ed0dae2788283d96e3cc0d0f4278 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Fri, 2 Dec 2022 17:32:13 +0000 Subject: [PATCH 13/51] Initial UI for setting up acknowledgement sending --- src/formSchema.js | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/formSchema.js b/src/formSchema.js index e351aad..b2f0796 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -45,12 +45,17 @@ const messages = defineMessages({ }, send: { id: 'form_send_email', - defaultMessage: 'Send email to recipient', + defaultMessage: 'Send email to', }, }); -export default () => { +export default (formData) => { var intl = useIntl(); + const emailFields = formData.subblocks.reduce((acc, field) => { + return ['from', 'email'].includes(field.field_type) + ? [...acc, [field.id, field.label]] + : acc; + }, []); return { title: intl.formatMessage(messages.form), @@ -68,6 +73,9 @@ export default () => { 'captcha', 'store', 'send', + ...(formData.send.includes('acknowledgement') + ? ['acknowledgementFields', 'acknowledgementMessage'] + : []), ], }, ], @@ -104,8 +112,28 @@ export default () => { description: intl.formatMessage(messages.attachmentSendEmail), }, send: { - type: 'boolean', title: intl.formatMessage(messages.send), + isMulti: 'true', + default: 'recipient', + choices: [ + ['recipient', 'Recipient'], + ['acknowledgement', 'Acknowledgement'], + ], + }, + acknowledgementMessage: { + // TODO: i18n + title: 'Acknowledgement message', + widget: 'richtext', + }, + acknowledgementFields: { + // TODO: i18n + title: 'Acknowledgement field', + decription: + 'Select which fields will contain an email address to send an acknowledgement to.', + isMulti: false, + noValueOption: false, + choices: formData.subblocks ? emailFields : [], + ...(emailFields.length === 1 && { default: emailFields[0][0] }), }, }, required: ['default_to', 'default_from', 'default_subject'], From 6b520659cded06ab34cae24821a48910f957cb41 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Tue, 6 Dec 2022 16:48:53 +0000 Subject: [PATCH 14/51] Fix crash creating form --- src/formSchema.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/formSchema.js b/src/formSchema.js index b2f0796..e4519c8 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -51,11 +51,12 @@ const messages = defineMessages({ export default (formData) => { var intl = useIntl(); - const emailFields = formData.subblocks.reduce((acc, field) => { - return ['from', 'email'].includes(field.field_type) - ? [...acc, [field.id, field.label]] - : acc; - }, []); + const emailFields = + formData.subblocks?.reduce((acc, field) => { + return ['from', 'email'].includes(field.field_type) + ? [...acc, [field.id, field.label]] + : acc; + }, []) ?? []; return { title: intl.formatMessage(messages.form), From 362ebda00a70535572c33ad066f7cdd18b821a71 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Tue, 6 Dec 2022 16:50:23 +0000 Subject: [PATCH 15/51] Fix another crash --- src/formSchema.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/formSchema.js b/src/formSchema.js index e4519c8..aaf477c 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -74,7 +74,7 @@ export default (formData) => { 'captcha', 'store', 'send', - ...(formData.send.includes('acknowledgement') + ...(formData.send?.includes('acknowledgement') ? ['acknowledgementFields', 'acknowledgementMessage'] : []), ], From 9f5907aca30932a9cb5e2bb1b2af70546e77f576 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Mon, 12 Dec 2022 17:14:48 +0000 Subject: [PATCH 16/51] Fix crash on block creation --- src/formSchema.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/formSchema.js b/src/formSchema.js index aaf477c..2180276 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -52,7 +52,7 @@ const messages = defineMessages({ export default (formData) => { var intl = useIntl(); const emailFields = - formData.subblocks?.reduce((acc, field) => { + formData?.subblocks?.reduce((acc, field) => { return ['from', 'email'].includes(field.field_type) ? [...acc, [field.id, field.label]] : acc; @@ -74,7 +74,7 @@ export default (formData) => { 'captcha', 'store', 'send', - ...(formData.send?.includes('acknowledgement') + ...(formData?.send?.includes('acknowledgement') ? ['acknowledgementFields', 'acknowledgementMessage'] : []), ], @@ -133,7 +133,7 @@ export default (formData) => { 'Select which fields will contain an email address to send an acknowledgement to.', isMulti: false, noValueOption: false, - choices: formData.subblocks ? emailFields : [], + choices: formData?.subblocks ? emailFields : [], ...(emailFields.length === 1 && { default: emailFields[0][0] }), }, }, From 9d3ec317e87647d3614818dd7ef451be321d8c04 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Mon, 12 Dec 2022 17:15:57 +0000 Subject: [PATCH 17/51] Fix crash on block creation --- src/formSchema.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/formSchema.js b/src/formSchema.js index fcf8d43..ad95d4d 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -85,11 +85,11 @@ export default (formData) => { }); } - if (formData.send || formData.store) { + if (formData?.send || formData?.store) { fieldsets.push({ id: 'storedDataIds', title: intl.formatMessage(messages.storedDataIds), - fields: formData.subblocks.map((subblock) => subblock.field_id), + fields: formData?.subblocks.map((subblock) => subblock.field_id), }); } @@ -137,10 +137,10 @@ export default (formData) => { title: intl.formatMessage(messages.attachXml), }, // Add properties for each of the fields for use in the data mapping - ...(formData.subblocks + ...(formData?.subblocks ? Object.assign( {}, - ...formData.subblocks.map((subblock) => { + ...formData?.subblocks.map((subblock) => { return { [subblock.field_id]: { title: subblock.label } }; }), ) From eb1e5470fd935f68f7d6462ff264b435d4199873 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Tue, 13 Dec 2022 07:57:40 +0000 Subject: [PATCH 18/51] Fix missing conditional --- src/formSchema.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/formSchema.js b/src/formSchema.js index ad95d4d..bc8a5f4 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -77,7 +77,7 @@ export default (formData) => { }, ]; - if (formData.send) { + if (formData?.send) { fieldsets.push({ id: 'sendingOptions', title: 'Sending options', From 6530103b7e2557b7e08e3713e2f33025f4954e72 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Tue, 13 Dec 2022 07:59:18 +0000 Subject: [PATCH 19/51] Fix crash when opening existing forms --- src/formSchema.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/formSchema.js b/src/formSchema.js index 2180276..48554d6 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -1,5 +1,4 @@ -import { defineMessages } from 'react-intl'; -import { useIntl } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; const messages = defineMessages({ form: { @@ -74,7 +73,9 @@ export default (formData) => { 'captcha', 'store', 'send', - ...(formData?.send?.includes('acknowledgement') + ...(formData?.send && + Array.isArray(formData.send) && + formData.send.includes('acknowledgement') ? ['acknowledgementFields', 'acknowledgementMessage'] : []), ], From 83968943bf9de66604069e0eead9cffce1a352fc Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Sun, 5 Feb 2023 23:53:54 +0000 Subject: [PATCH 20/51] Fix crash on block creation --- src/formSchema.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/formSchema.js b/src/formSchema.js index bc8a5f4..fb2c655 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -89,7 +89,7 @@ export default (formData) => { fieldsets.push({ id: 'storedDataIds', title: intl.formatMessage(messages.storedDataIds), - fields: formData?.subblocks.map((subblock) => subblock.field_id), + fields: formData?.subblocks?.map((subblock) => subblock.field_id), }); } @@ -140,7 +140,7 @@ export default (formData) => { ...(formData?.subblocks ? Object.assign( {}, - ...formData?.subblocks.map((subblock) => { + ...formData?.subblocks?.map((subblock) => { return { [subblock.field_id]: { title: subblock.label } }; }), ) From 8f2bdce9e09f0b105f98b136b8e268864278ccd7 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 8 Mar 2023 09:26:49 +0000 Subject: [PATCH 21/51] Add UI for selecting HTTP headers to forward in email --- src/formSchema.js | 68 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/src/formSchema.js b/src/formSchema.js index e351aad..936a153 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -34,7 +34,10 @@ const messages = defineMessages({ id: 'captcha', defaultMessage: 'Captcha provider', }, - + headers: { + id: 'Headers', + defaultMessage: 'Headers', + }, store: { id: 'form_save_persistent_data', defaultMessage: 'Store compiled data', @@ -49,28 +52,38 @@ const messages = defineMessages({ }, }); -export default () => { +export default (formData) => { var intl = useIntl(); + const fieldsets = [ + { + id: 'default', + title: 'Default', + fields: [ + 'title', + 'description', + 'default_to', + 'default_from', + 'default_subject', + 'submit_label', + 'captcha', + 'store', + 'send', + ], + }, + ]; + + if (formData?.send) { + fieldsets.push({ + id: 'sendingOptions', + title: 'Sending options', + fields: ['httpHeaders'], + }); + } + return { title: intl.formatMessage(messages.form), - fieldsets: [ - { - id: 'default', - title: 'Default', - fields: [ - 'title', - 'description', - 'default_to', - 'default_from', - 'default_subject', - 'submit_label', - 'captcha', - 'store', - 'send', - ], - }, - ], + fieldsets: fieldsets, properties: { title: { title: intl.formatMessage(messages.title), @@ -107,6 +120,23 @@ export default () => { type: 'boolean', title: intl.formatMessage(messages.send), }, + httpHeaders: { + type: 'boolean', + title: intl.formatMessage(messages.headers), + type: 'string', + factory: 'Choice', + default: '', + isMulti: true, + noValueOption: false, + choices: [ + ['HTTP_X_FORWARDED_FOR','HTTP_X_FORWARDED_FOR'], + ['HTTP_X_FORWARDED_PORT','HTTP_X_FORWARDED_PORT'], + ['REMOTE_ADDR','REMOTE_ADDR'], + ['PATH_INFO','PATH_INFO'], + ['HTTP_USER_AGENT','HTTP_USER_AGENT'], + ['HTTP_REFERER','HTTP_REFERER'], + ], + }, }, required: ['default_to', 'default_from', 'default_subject'], }; From 1aa4687807d815398068c2c3a0f02b98e5e20ee8 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 8 Mar 2023 09:28:45 +0000 Subject: [PATCH 22/51] Add description --- src/formSchema.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/formSchema.js b/src/formSchema.js index 936a153..d648030 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -38,6 +38,10 @@ const messages = defineMessages({ id: 'Headers', defaultMessage: 'Headers', }, + headersDescription: { + id: 'Headers Description', + defaultMessage: "These headers aren't included in the sent email by default. Use this dropdown to include them in the sent email", + }, store: { id: 'form_save_persistent_data', defaultMessage: 'Store compiled data', @@ -123,6 +127,7 @@ export default (formData) => { httpHeaders: { type: 'boolean', title: intl.formatMessage(messages.headers), + description: intl.formatMessage(messages.headersDescription), type: 'string', factory: 'Choice', default: '', From 78f61b6c6e0bb9d0d5e514eb3d59a5597bb8ea41 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Tue, 25 Apr 2023 16:37:17 +0100 Subject: [PATCH 23/51] Add option to change email format --- src/formSchema.js | 59 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/src/formSchema.js b/src/formSchema.js index e351aad..35925c2 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -47,30 +47,44 @@ const messages = defineMessages({ id: 'form_send_email', defaultMessage: 'Send email to recipient', }, + email_format: { + id: 'form_email_format', + defaultMessage: 'Email format', + }, }); -export default () => { +export default (formData) => { var intl = useIntl(); + const fieldsets = [ + { + id: 'default', + title: 'Default', + fields: [ + 'title', + 'description', + 'default_to', + 'default_from', + 'default_subject', + 'submit_label', + 'captcha', + 'store', + 'send', + ], + }, + ]; + + if (formData?.send) { + fieldsets.push({ + id: 'sendingOptions', + title: 'Sending options', + fields: ['email_format'], + }); + } + return { title: intl.formatMessage(messages.form), - fieldsets: [ - { - id: 'default', - title: 'Default', - fields: [ - 'title', - 'description', - 'default_to', - 'default_from', - 'default_subject', - 'submit_label', - 'captcha', - 'store', - 'send', - ], - }, - ], + fieldsets: fieldsets, properties: { title: { title: intl.formatMessage(messages.title), @@ -107,6 +121,15 @@ export default () => { type: 'boolean', title: intl.formatMessage(messages.send), }, + email_format: { + title: intl.formatMessage(messages.email_format), + type: 'string', + choices: [ + ['list', 'List'], + ['table', 'Table'], + ], + noValueOption: false, + }, }, required: ['default_to', 'default_from', 'default_subject'], }; From ffc5c22d255269d43598113f8e4d9ebdd2688eea Mon Sep 17 00:00:00 2001 From: Jefferson Bledsoe Date: Wed, 26 Apr 2023 09:32:11 +0100 Subject: [PATCH 24/51] Use string for field_type --- src/fieldSchema.js | 116 ++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/src/fieldSchema.js b/src/fieldSchema.js index 95916b5..80f625d 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -1,92 +1,92 @@ -import config from '@plone/volto/registry'; -import { defineMessages } from 'react-intl'; -import { useIntl } from 'react-intl'; +import config from "@plone/volto/registry"; +import { defineMessages } from "react-intl"; +import { useIntl } from "react-intl"; const messages = defineMessages({ field_label: { - id: 'form_field_label', - defaultMessage: 'Label', + id: "form_field_label", + defaultMessage: "Label", }, field_description: { - id: 'form_field_description', - defaultMessage: 'Description', + id: "form_field_description", + defaultMessage: "Description", }, field_required: { - id: 'form_field_required', - defaultMessage: 'Required', + id: "form_field_required", + defaultMessage: "Required", }, field_type: { - id: 'form_field_type', - defaultMessage: 'Field type', + id: "form_field_type", + defaultMessage: "Field type", }, field_type_text: { - id: 'form_field_type_text', - defaultMessage: 'Text', + id: "form_field_type_text", + defaultMessage: "Text", }, field_type_textarea: { - id: 'form_field_type_textarea', - defaultMessage: 'Textarea', + id: "form_field_type_textarea", + defaultMessage: "Textarea", }, field_type_select: { - id: 'form_field_type_select', - defaultMessage: 'List', + id: "form_field_type_select", + defaultMessage: "List", }, field_type_single_choice: { - id: 'form_field_type_single_choice', - defaultMessage: 'Single choice', + id: "form_field_type_single_choice", + defaultMessage: "Single choice", }, field_type_multiple_choice: { - id: 'form_field_type_multiple_choice', - defaultMessage: 'Multiple choice', + id: "form_field_type_multiple_choice", + defaultMessage: "Multiple choice", }, field_type_checkbox: { - id: 'form_field_type_checkbox', - defaultMessage: 'Checkbox', + id: "form_field_type_checkbox", + defaultMessage: "Checkbox", }, field_type_date: { - id: 'form_field_type_date', - defaultMessage: 'Date', + id: "form_field_type_date", + defaultMessage: "Date", }, field_type_attachment: { - id: 'form_field_type_attachment', - defaultMessage: 'Attachment', + id: "form_field_type_attachment", + defaultMessage: "Attachment", }, field_type_attachment_info_text: { - id: 'form_field_type_attachment_info_text', - defaultMessage: 'Any attachments can be emailed, but will not be saved.', + id: "form_field_type_attachment_info_text", + defaultMessage: "Any attachments can be emailed, but will not be saved.", }, field_type_from: { - id: 'form_field_type_from', - defaultMessage: 'E-mail', + id: "form_field_type_from", + defaultMessage: "E-mail", }, field_type_static_text: { - id: 'form_field_type_static_text', - defaultMessage: 'Static text', + id: "form_field_type_static_text", + defaultMessage: "Static text", }, }); export default (props) => { var intl = useIntl(); const baseFieldTypeChoices = [ - ['text', intl.formatMessage(messages.field_type_text)], - ['textarea', intl.formatMessage(messages.field_type_textarea)], - ['select', intl.formatMessage(messages.field_type_select)], - ['single_choice', intl.formatMessage(messages.field_type_single_choice)], + ["text", intl.formatMessage(messages.field_type_text)], + ["textarea", intl.formatMessage(messages.field_type_textarea)], + ["select", intl.formatMessage(messages.field_type_select)], + ["single_choice", intl.formatMessage(messages.field_type_single_choice)], [ - 'multiple_choice', + "multiple_choice", intl.formatMessage(messages.field_type_multiple_choice), ], - ['checkbox', intl.formatMessage(messages.field_type_checkbox)], - ['date', intl.formatMessage(messages.field_type_date)], - ['attachment', intl.formatMessage(messages.field_type_attachment)], - ['from', intl.formatMessage(messages.field_type_from)], - ['static_text', intl.formatMessage(messages.field_type_static_text)], + ["checkbox", intl.formatMessage(messages.field_type_checkbox)], + ["date", intl.formatMessage(messages.field_type_date)], + ["attachment", intl.formatMessage(messages.field_type_attachment)], + ["from", intl.formatMessage(messages.field_type_from)], + ["static_text", intl.formatMessage(messages.field_type_static_text)], ]; var attachmentDescription = - props?.field_type === 'attachment' + props?.field_type === "attachment" ? { description: intl.formatMessage( - messages.field_type_attachment_info_text, + messages.field_type_attachment_info_text ), } : {}; @@ -97,17 +97,17 @@ export default (props) => { ? schemaExtender(intl) : { properties: [], fields: [], required: [] }; return { - title: props?.label || '', + title: props?.label || "", fieldsets: [ { - id: 'default', - title: 'Default', + id: "default", + title: "Default", fields: [ - 'label', - 'description', - 'field_type', + "label", + "description", + "field_type", ...schemaExtenderValues.fields, - 'required', + "required", ], }, ], @@ -122,26 +122,26 @@ export default (props) => { }, field_type: { title: intl.formatMessage(messages.field_type), - type: 'array', + type: "string", choices: [ ...baseFieldTypeChoices, ...(config.blocks.blocksConfig.form.additionalFields?.map( - (fieldType) => [fieldType.id, fieldType.label], + (fieldType) => [fieldType.id, fieldType.label] ) ?? []), ], ...attachmentDescription, }, required: { title: intl.formatMessage(messages.field_required), - type: 'boolean', + type: "boolean", default: false, }, ...schemaExtenderValues.properties, }, required: [ - 'label', - 'field_type', - 'input_values', + "label", + "field_type", + "input_values", ...schemaExtenderValues.required, ], }; From b36751e1942306d59118da94a2feeb05540c5b57 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Thu, 27 Apr 2023 22:04:04 +0100 Subject: [PATCH 25/51] Initial UI & implementation of 'show_when' --- src/components/Field.jsx | 3 ++ src/components/FormView.jsx | 31 ++++++++++++++--- src/components/Sidebar.jsx | 2 +- src/fieldSchema.js | 66 ++++++++++++++++++++++++++++++++++++- src/helpers/show_when.js | 10 ++++++ 5 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 src/helpers/show_when.js diff --git a/src/components/Field.jsx b/src/components/Field.jsx index a5a022b..26e7484 100644 --- a/src/components/Field.jsx +++ b/src/components/Field.jsx @@ -44,9 +44,12 @@ const Field = ({ disabled = false, formHasErrors = false, id, + ...rest }) => { const intl = useIntl(); + console.log('rest', rest); + const isInvalid = () => { return !isOnEdit && !valid; }; diff --git a/src/components/FormView.jsx b/src/components/FormView.jsx index db65cea..b44569f 100644 --- a/src/components/FormView.jsx +++ b/src/components/FormView.jsx @@ -10,6 +10,7 @@ import { } from 'semantic-ui-react'; import { getFieldName } from 'volto-form-block/components/utils'; import Field from 'volto-form-block/components/Field'; +import { showWhenValidator } from 'volto-form-block/helpers/show_when'; import config from '@plone/volto/registry'; /* Style */ @@ -134,6 +135,30 @@ const FormView = ({ }), ); + const value = + subblock.field_type === 'static_text' + ? subblock.value + : formData[name]?.value; + const { show_when, target_value } = subblock; + + const shouldShowValidator = showWhenValidator[show_when]; + const shouldShowTargetValue = + formData[subblock.target_field]?.value; + + // Only checking for false here to preserve backwards compatibility with blocks that haven't been updated and so have a value of 'undefined' or 'null' + const shouldShow = shouldShowValidator + ? shouldShowValidator({ + value: shouldShowTargetValue, + target_value: target_value, + }) !== false + : true; + + const shouldHide = __CLIENT__ && !shouldShow; + + if (shouldHide) { + return

Empty

; + } + return ( @@ -148,11 +173,7 @@ const FormView = ({ fields_to_send_with_value, ) } - value={ - subblock.field_type === 'static_text' - ? subblock.value - : formData[name]?.value - } + value={value} valid={isValidField(name)} formHasErrors={formErrors?.length > 0} /> diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 7edf0b2..e2696cd 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -191,7 +191,7 @@ const Sidebar = ({ { var update_values = {}; diff --git a/src/fieldSchema.js b/src/fieldSchema.js index 95916b5..0ae6771 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -1,6 +1,7 @@ import config from '@plone/volto/registry'; import { defineMessages } from 'react-intl'; import { useIntl } from 'react-intl'; +import { getFieldName } from 'volto-form-block/components/utils'; const messages = defineMessages({ field_label: { @@ -63,6 +64,22 @@ const messages = defineMessages({ id: 'form_field_type_static_text', defaultMessage: 'Static text', }, + field_show_when: { + id: 'form_field_show_when', + defaultMessage: 'Show when', + }, + field_show_when_option_always: { + id: 'form_field_show_when_option_', + defaultMessage: 'Always', + }, + field_show_when_option_value_is: { + id: 'form_field_show_when_option_value_is', + defaultMessage: 'Value is', + }, + field_show_when_option_value_is_not: { + id: 'form_field_show_when_option_value_is_not', + defaultMessage: 'Value is not', + }, }); export default (props) => { @@ -96,6 +113,7 @@ export default (props) => { const schemaExtenderValues = schemaExtender ? schemaExtender(intl) : { properties: [], fields: [], required: [] }; + return { title: props?.label || '', fieldsets: [ @@ -108,6 +126,9 @@ export default (props) => { 'field_type', ...schemaExtenderValues.fields, 'required', + 'show_when', + ...((props.show_when === 'value_is' || props.show_when === 'value_is_not') ? ['target_field'] : []), + ...((props.show_when === 'value_is' || props.show_when === 'value_is_not') ? ['target_value'] : []), ], }, ], @@ -122,7 +143,7 @@ export default (props) => { }, field_type: { title: intl.formatMessage(messages.field_type), - type: 'array', + type: 'string', choices: [ ...baseFieldTypeChoices, ...(config.blocks.blocksConfig.form.additionalFields?.map( @@ -136,6 +157,49 @@ export default (props) => { type: 'boolean', default: false, }, + show_when: { + title: intl.formatMessage(messages.field_show_when), + type: 'string', + choices: [ + [ + 'always', + intl.formatMessage(messages.field_show_when_option_always), + ], + [ + 'value_is', + intl.formatMessage(messages.field_show_when_option_value_is), + ], + [ + 'value_is_not', + intl.formatMessage(messages.field_show_when_option_value_is_not), + ], + ], + noValueOption: false, + }, + // TODO: i18n + target_field: { + title: 'Target field', + type: 'string', + choices: [ + // Add properties for each of the fields for use in the data mapping + ...(props?.formData?.subblocks + ? props.formData.subblocks.map((subblock) => { + // Using getFieldName as it is what is used for the formData later. Saves + // performing `getFieldName` for every block every render. + return [ + getFieldName(subblock.label, subblock.field_id), + subblock.label, + ]; + }) + : []), + ], + noValueOption: false, + }, + // TODO: i18n + target_value: { + title: 'Target value', + type: 'string', + }, ...schemaExtenderValues.properties, }, required: [ diff --git a/src/helpers/show_when.js b/src/helpers/show_when.js new file mode 100644 index 0000000..1e6ad93 --- /dev/null +++ b/src/helpers/show_when.js @@ -0,0 +1,10 @@ +const always = () => true; +const value_is = ({ value, target_value }) => value === target_value; +const value_is_not = ({ value, target_value }) => value !== target_value; + +export const showWhenValidator = { + '': always, + always: always, + value_is: value_is, + value_is_not: value_is_not, +}; From eb16a64270fa5451718d7d3aebcc1e5201572fcf Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 3 May 2023 17:39:12 +0100 Subject: [PATCH 26/51] Return to using only field ID for target field value --- src/fieldSchema.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/fieldSchema.js b/src/fieldSchema.js index 0ae6771..7f97f72 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -1,7 +1,6 @@ import config from '@plone/volto/registry'; import { defineMessages } from 'react-intl'; import { useIntl } from 'react-intl'; -import { getFieldName } from 'volto-form-block/components/utils'; const messages = defineMessages({ field_label: { @@ -74,11 +73,11 @@ const messages = defineMessages({ }, field_show_when_option_value_is: { id: 'form_field_show_when_option_value_is', - defaultMessage: 'Value is', + defaultMessage: 'value is', }, field_show_when_option_value_is_not: { id: 'form_field_show_when_option_value_is_not', - defaultMessage: 'Value is not', + defaultMessage: 'value is not', }, }); @@ -127,8 +126,14 @@ export default (props) => { ...schemaExtenderValues.fields, 'required', 'show_when', - ...((props.show_when === 'value_is' || props.show_when === 'value_is_not') ? ['target_field'] : []), - ...((props.show_when === 'value_is' || props.show_when === 'value_is_not') ? ['target_value'] : []), + ...(props.show_when === 'value_is' || + props.show_when === 'value_is_not' + ? ['target_field'] + : []), + ...(props.show_when === 'value_is' || + props.show_when === 'value_is_not' + ? ['target_value'] + : []), ], }, ], @@ -186,10 +191,7 @@ export default (props) => { ? props.formData.subblocks.map((subblock) => { // Using getFieldName as it is what is used for the formData later. Saves // performing `getFieldName` for every block every render. - return [ - getFieldName(subblock.label, subblock.field_id), - subblock.label, - ]; + return [subblock.field_id, subblock.label]; }) : []), ], From aa47bdc6d56b947bee1807edcae57c5feff4d2bf Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 17 May 2023 12:58:40 +0100 Subject: [PATCH 27/51] Update UI --- src/fieldSchema.js | 71 +++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/src/fieldSchema.js b/src/fieldSchema.js index 7f97f72..f46df71 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -63,9 +63,21 @@ const messages = defineMessages({ id: 'form_field_type_static_text', defaultMessage: 'Static text', }, - field_show_when: { + field_type_hidden: { + id: 'form_field_type_hidden', + defaultMessage: 'Hidden', + }, + field_show_when_when: { id: 'form_field_show_when', - defaultMessage: 'Show when', + defaultMessage: 'When', + }, + field_show_when_is: { + id: 'form_field_show_is', + defaultMessage: 'Is', + }, + field_show_when_to: { + id: 'form_field_show_to', + defaultMessage: 'To', }, field_show_when_option_always: { id: 'form_field_show_when_option_', @@ -73,11 +85,11 @@ const messages = defineMessages({ }, field_show_when_option_value_is: { id: 'form_field_show_when_option_value_is', - defaultMessage: 'value is', + defaultMessage: 'equal', }, field_show_when_option_value_is_not: { id: 'form_field_show_when_option_value_is_not', - defaultMessage: 'value is not', + defaultMessage: 'not equal', }, }); @@ -125,14 +137,12 @@ export default (props) => { 'field_type', ...schemaExtenderValues.fields, 'required', - 'show_when', - ...(props.show_when === 'value_is' || - props.show_when === 'value_is_not' - ? ['target_field'] + 'show_when_when', + ...(props.show_when_when && props.show_when_when !== 'always' + ? ['show_when_is'] : []), - ...(props.show_when === 'value_is' || - props.show_when === 'value_is_not' - ? ['target_value'] + ...(props.show_when_when && props.show_when_when !== 'always' + ? ['show_when_to'] : []), ], }, @@ -162,14 +172,28 @@ export default (props) => { type: 'boolean', default: false, }, - show_when: { - title: intl.formatMessage(messages.field_show_when), + show_when_when: { + title: intl.formatMessage(messages.field_show_when_when), type: 'string', choices: [ [ 'always', intl.formatMessage(messages.field_show_when_option_always), ], + ...(props?.formData?.subblocks + ? props.formData.subblocks.map((subblock) => { + // Using getFieldName as it is what is used for the formData later. Saves + // performing `getFieldName` for every block every render. + return [subblock.field_id, subblock.label]; + }) + : []), + ], + default: 'always', + }, + show_when_is: { + title: intl.formatMessage(messages.field_show_when_is), + type: 'string', + choices: [ [ 'value_is', intl.formatMessage(messages.field_show_when_option_value_is), @@ -181,25 +205,8 @@ export default (props) => { ], noValueOption: false, }, - // TODO: i18n - target_field: { - title: 'Target field', - type: 'string', - choices: [ - // Add properties for each of the fields for use in the data mapping - ...(props?.formData?.subblocks - ? props.formData.subblocks.map((subblock) => { - // Using getFieldName as it is what is used for the formData later. Saves - // performing `getFieldName` for every block every render. - return [subblock.field_id, subblock.label]; - }) - : []), - ], - noValueOption: false, - }, - // TODO: i18n - target_value: { - title: 'Target value', + show_when_to: { + title: intl.formatMessage(messages.field_show_when_to), type: 'string', }, ...schemaExtenderValues.properties, From 1b1f80741dca10c209eca998e2b2b10176c9bcd1 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 17 May 2023 13:15:48 +0100 Subject: [PATCH 28/51] Fix bad merge --- src/formSchema.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/formSchema.js b/src/formSchema.js index 6146831..fa708cd 100644 --- a/src/formSchema.js +++ b/src/formSchema.js @@ -115,23 +115,6 @@ export default (formData) => { }); } - const fieldsets = [ - { - id: 'default', - title: 'Default', - fields: [ - 'title', - 'description', - 'default_to', - 'default_from', - 'default_subject', - 'submit_label', - 'captcha', - 'store', - 'send', - ], - }, - ]; if (formData?.send) { fieldsets.push({ From d215ff44b197919d58f7590d5d5f9166c8fd6b76 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 24 May 2023 23:55:57 +0100 Subject: [PATCH 29/51] Send all fields and not just ones that haven't been filled out --- src/components/View.jsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/View.jsx b/src/components/View.jsx index 962335a..d230ad2 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -172,7 +172,18 @@ const View = ({ data, id, path }) => { captcha.value = formData[data.captcha_props.id]?.value ?? ''; } - let formattedFormData = { ...formData }; + let formattedFormData = data.subblocks.reduce( + (returnValue, field) => { + const fieldName = getFieldName(field.label, field.id); + const dataToAdd = formData[fieldName] ?? { + field_id: field.id, + label: field.label, + value: null, + }; + return { ...returnValue, [fieldName]: dataToAdd }; + }, + {}, + ); data.subblocks.forEach((subblock) => { let name = getFieldName(subblock.label, subblock.id); if (formattedFormData[name]?.value) { From 08bb75eed34d2300ccc4504715397374c9d3036d Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Thu, 25 May 2023 17:12:17 +0100 Subject: [PATCH 30/51] Don't show fields after the current field that aren't the current value for _when choices --- src/fieldSchema.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/fieldSchema.js b/src/fieldSchema.js index f46df71..cc1e703 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -181,11 +181,22 @@ export default (props) => { intl.formatMessage(messages.field_show_when_option_always), ], ...(props?.formData?.subblocks - ? props.formData.subblocks.map((subblock) => { - // Using getFieldName as it is what is used for the formData later. Saves - // performing `getFieldName` for every block every render. - return [subblock.field_id, subblock.label]; - }) + ? props.formData.subblocks.reduce((choices, subblock, index) => { + const currentFieldIndex = props.formData.subblocks.findIndex( + (field) => field.field_id === props.field_id, + ); + if (index > currentFieldIndex) { + if (props.show_when_when === subblock.field_id) { + choices.push([subblock.field_id, subblock.label]); + } + return choices; + } + if (subblock.field_id === props.field_id) { + return choices; + } + choices.push([subblock.field_id, subblock.label]); + return choices; + }, []) : []), ], default: 'always', @@ -204,10 +215,12 @@ export default (props) => { ], ], noValueOption: false, + required: true, }, show_when_to: { title: intl.formatMessage(messages.field_show_when_to), type: 'string', + required: true, }, ...schemaExtenderValues.properties, }, From 52c423defef308a3055db47f4217241f56e75598 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Thu, 25 May 2023 17:23:24 +0100 Subject: [PATCH 31/51] Required fields --- src/fieldSchema.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/fieldSchema.js b/src/fieldSchema.js index cc1e703..22d1dcb 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -228,6 +228,9 @@ export default (props) => { 'label', 'field_type', 'input_values', + ...(props.show_when_when && props.show_when_when !== 'always' + ? ['show_when_is', 'show_when_to'] + : []), ...schemaExtenderValues.required, ], }; From e2792b949f1eead86d25f791881a65c5aadf189c Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Fri, 26 May 2023 17:32:29 +0100 Subject: [PATCH 32/51] Allow multiple choices --- src/fieldSchema.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fieldSchema.js b/src/fieldSchema.js index 22d1dcb..83fbf40 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -219,7 +219,7 @@ export default (props) => { }, show_when_to: { title: intl.formatMessage(messages.field_show_when_to), - type: 'string', + type: 'array', required: true, }, ...schemaExtenderValues.properties, From a7573497b1bdcac43e1390525643be5429a21020 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Fri, 26 May 2023 17:32:50 +0100 Subject: [PATCH 33/51] Pre-fill choices if the targeted field is a 'chooser' --- src/fieldSchema.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/fieldSchema.js b/src/fieldSchema.js index 83fbf40..cba071b 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -93,6 +93,8 @@ const messages = defineMessages({ }, }); +const choiceTypes = ['select', 'single_choice', 'multiple_choice']; + export default (props) => { var intl = useIntl(); const baseFieldTypeChoices = [ @@ -125,6 +127,13 @@ export default (props) => { ? schemaExtender(intl) : { properties: [], fields: [], required: [] }; + const show_when_when_field = + props.show_when_when && props.show_when_when + ? props.formData?.subblocks?.find( + (field) => field.field_id === props.show_when_when, + ) + : undefined; + return { title: props?.label || '', fieldsets: [ @@ -221,6 +230,19 @@ export default (props) => { title: intl.formatMessage(messages.field_show_when_to), type: 'array', required: true, + creatable: true, + noValueOption: false, + ...(show_when_when_field && + choiceTypes.includes(show_when_when_field.field_type) && { + choices: show_when_when_field.input_values, + }), + ...(show_when_when_field && + show_when_when_field.field_type === 'yes_no' && { + choices: [ + [true, 'Yes'], + [false, 'No'], + ], + }), }, ...schemaExtenderValues.properties, }, From bb36e43e5868bbb36f36c34289cdcb0269b89806 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Fri, 26 May 2023 19:16:50 +0100 Subject: [PATCH 34/51] Update 'Show when' name --- src/fieldSchema.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fieldSchema.js b/src/fieldSchema.js index cba071b..a6f8e0c 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -69,7 +69,7 @@ const messages = defineMessages({ }, field_show_when_when: { id: 'form_field_show_when', - defaultMessage: 'When', + defaultMessage: 'Show when', }, field_show_when_is: { id: 'form_field_show_is', From 7b4e04a585165f79f44c69e1954362d1121581ef Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Mon, 19 Jun 2023 19:01:25 +0100 Subject: [PATCH 35/51] Don't attempt to send static text --- src/components/View.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/View.jsx b/src/components/View.jsx index d230ad2..9654552 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -174,6 +174,9 @@ const View = ({ data, id, path }) => { let formattedFormData = data.subblocks.reduce( (returnValue, field) => { + if (field.field_type === 'static_text') { + return returnValue; + } const fieldName = getFieldName(field.label, field.id); const dataToAdd = formData[fieldName] ?? { field_id: field.id, From ee21bed5d5089c557d9f3657f40e41df6962bb6a Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Tue, 20 Jun 2023 13:33:51 +0100 Subject: [PATCH 36/51] Ensure custom field mappings are used for empty field submissions --- src/components/View.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/View.jsx b/src/components/View.jsx index 9654552..52b3759 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -182,6 +182,7 @@ const View = ({ data, id, path }) => { field_id: field.id, label: field.label, value: null, + ...(data[field.id] && { custom_field_id: data[field.id] }), // Conditionally add the key. Nicer to work with than having a key with a null value }; return { ...returnValue, [fieldName]: dataToAdd }; }, From 69f99112183164e1ca50aa5689b7b2282eec88ac Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 26 Jul 2023 00:21:42 +0100 Subject: [PATCH 37/51] Don't pre-format dates --- src/components/View.jsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/components/View.jsx b/src/components/View.jsx index 8ee87be..0509681 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -195,20 +195,11 @@ const View = ({ data, id, path }) => { const isAttachment = config.blocks.blocksConfig.form.attachment_fields.includes( subblock.field_type, ); - const isDate = subblock.field_type === 'date'; if (isAttachment) { attachments[name] = formattedFormData[name].value; delete formattedFormData[name]; } - - if (isDate) { - formattedFormData[name].value = formatDate({ - date: formattedFormData[name].value, - format: 'DD-MM-YYYY', - locale: intl.locale, - }); - } } }); dispatch( From 2d59a95769b6c9e9d028d906ac3948dd903c010b Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 30 Aug 2023 01:31:26 +0100 Subject: [PATCH 38/51] Revert "Don't pre-format dates" This reverts commit 69f99112183164e1ca50aa5689b7b2282eec88ac. --- src/components/View.jsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/components/View.jsx b/src/components/View.jsx index 0509681..8ee87be 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -195,11 +195,20 @@ const View = ({ data, id, path }) => { const isAttachment = config.blocks.blocksConfig.form.attachment_fields.includes( subblock.field_type, ); + const isDate = subblock.field_type === 'date'; if (isAttachment) { attachments[name] = formattedFormData[name].value; delete formattedFormData[name]; } + + if (isDate) { + formattedFormData[name].value = formatDate({ + date: formattedFormData[name].value, + format: 'DD-MM-YYYY', + locale: intl.locale, + }); + } } }); dispatch( From f06ac094e1c6b6766a2543ba5a85d5f764620f0d Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 30 Aug 2023 02:47:31 +0100 Subject: [PATCH 39/51] Revert "Revert "Don't pre-format dates"" This reverts commit 2d59a95769b6c9e9d028d906ac3948dd903c010b. --- src/components/View.jsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/components/View.jsx b/src/components/View.jsx index 8ee87be..0509681 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -195,20 +195,11 @@ const View = ({ data, id, path }) => { const isAttachment = config.blocks.blocksConfig.form.attachment_fields.includes( subblock.field_type, ); - const isDate = subblock.field_type === 'date'; if (isAttachment) { attachments[name] = formattedFormData[name].value; delete formattedFormData[name]; } - - if (isDate) { - formattedFormData[name].value = formatDate({ - date: formattedFormData[name].value, - format: 'DD-MM-YYYY', - locale: intl.locale, - }); - } } }); dispatch( From e6dc376d2d8af288b209361c53eb25b859c77e9f Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Thu, 7 Sep 2023 17:26:26 +0100 Subject: [PATCH 40/51] WIP: Yes/ no internal value --- .../YesNoSchemaExtender.js | 38 +++++++++++++++++-- src/fieldSchema.js | 2 +- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js b/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js index 99b5062..467f205 100644 --- a/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js +++ b/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js @@ -6,19 +6,51 @@ const messages = defineMessages({ }, }); -export const YesNoSchemaExtender = (intl) => { +function InternalValueSchema() { return { - fields: ['widget'], + title: 'Test', + fieldsets: [ + { + id: 'default', + title: 'Default', + fields: ['yes', 'no'], + }, + ], + properties: { + no: { + title: 'no', + default: 'Definitely no', + }, + yes: { + title: 'yes', + default: 'Definitely yes', + }, + }, + }; +} + +export const YesNoSchemaExtender = ({ intl, formData, ...props }) => { + return { + fields: + props.widget === 'single_choice' + ? ['widget', 'internal_value'] + : ['widget'], properties: { widget: { title: intl.formatMessage(messages.field_widget), - type: 'array', + type: 'string', choices: [ ['checkbox', 'Checkbox'], ['single_choice', 'Radio'], ], default: 'checkbox', }, + internal_value: { + title: 'Internal value', + widget: 'object', + schema: InternalValueSchema(), + collapsible: true, + }, }, required: ['widget'], }; diff --git a/src/fieldSchema.js b/src/fieldSchema.js index 5e228f4..4b85343 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -125,7 +125,7 @@ export default (props) => { var schemaExtender = config.blocks.blocksConfig.form.fieldTypeSchemaExtenders[props?.field_type]; const schemaExtenderValues = schemaExtender - ? schemaExtender(intl) + ? schemaExtender({ intl, ...props }) : { properties: [], fields: [], required: [] }; const show_when_when_field = From 5f9827c703aab66c1710a5904ec2c1ebce41010b Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Mon, 11 Sep 2023 09:32:24 +0100 Subject: [PATCH 41/51] Send as display value not internal value --- .../FieldTypeSchemaExtenders/YesNoSchemaExtender.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js b/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js index 467f205..e137bce 100644 --- a/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js +++ b/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js @@ -17,13 +17,11 @@ function InternalValueSchema() { }, ], properties: { - no: { - title: 'no', - default: 'Definitely no', - }, yes: { - title: 'yes', - default: 'Definitely yes', + title: 'True', + }, + no: { + title: 'False', }, }, }; From 0b29f43e30a548db2676a1e2d09a28ce85fc2776 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Tue, 19 Sep 2023 17:25:30 +0100 Subject: [PATCH 42/51] Fix intl destructure --- src/components/FieldTypeSchemaExtenders/FromSchemaExtender.js | 2 +- src/components/FieldTypeSchemaExtenders/HiddenSchemaExtender.js | 2 +- .../FieldTypeSchemaExtenders/SelectionSchemaExtender.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/FieldTypeSchemaExtenders/FromSchemaExtender.js b/src/components/FieldTypeSchemaExtenders/FromSchemaExtender.js index 033e82e..f6702a6 100644 --- a/src/components/FieldTypeSchemaExtenders/FromSchemaExtender.js +++ b/src/components/FieldTypeSchemaExtenders/FromSchemaExtender.js @@ -24,7 +24,7 @@ const messages = defineMessages({ }, }); -export const FromSchemaExtender = (intl) => { +export const FromSchemaExtender = ({ intl }) => { return { fields: ['use_as_reply_to', 'use_as_bcc'], properties: { diff --git a/src/components/FieldTypeSchemaExtenders/HiddenSchemaExtender.js b/src/components/FieldTypeSchemaExtenders/HiddenSchemaExtender.js index 48e89c6..714096f 100644 --- a/src/components/FieldTypeSchemaExtenders/HiddenSchemaExtender.js +++ b/src/components/FieldTypeSchemaExtenders/HiddenSchemaExtender.js @@ -6,7 +6,7 @@ const messages = defineMessages({ }, }); -export const HiddenSchemaExtender = (intl) => { +export const HiddenSchemaExtender = ({ intl }) => { return { fields: ['value'], properties: { diff --git a/src/components/FieldTypeSchemaExtenders/SelectionSchemaExtender.js b/src/components/FieldTypeSchemaExtenders/SelectionSchemaExtender.js index 98488aa..567f131 100644 --- a/src/components/FieldTypeSchemaExtenders/SelectionSchemaExtender.js +++ b/src/components/FieldTypeSchemaExtenders/SelectionSchemaExtender.js @@ -6,7 +6,7 @@ const messages = defineMessages({ }, }); -export const SelectionSchemaExtender = (intl) => { +export const SelectionSchemaExtender = ({ intl }) => { return { fields: ['input_values'], properties: { From 1266b4c99ccc43d8bfd2fa502b80d02b9158c000 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 27 Sep 2023 01:12:20 +0100 Subject: [PATCH 43/51] Updated UI for YesNo display_values --- .../YesNoSchemaExtender.js | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js b/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js index e137bce..ad74f55 100644 --- a/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js +++ b/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js @@ -4,6 +4,15 @@ const messages = defineMessages({ id: 'form_field_widget', defaultMessage: 'Widget', }, + display_values_title: { + id: 'form_field_display_values_title', + defaultMessage: 'Display values as', + }, + display_values_description: { + id: 'form_field_display_values_description', + defaultMessage: + 'Change how values appear in forms and emails. Data stores and sent, such as CSV exports and XML attachments, will remain unchanged.', + }, }); function InternalValueSchema() { @@ -19,20 +28,19 @@ function InternalValueSchema() { properties: { yes: { title: 'True', + placeholder: 'Yes', }, no: { title: 'False', + placeholder: 'No', }, }, }; } -export const YesNoSchemaExtender = ({ intl, formData, ...props }) => { +export const YesNoSchemaExtender = ({ intl, formData }) => { return { - fields: - props.widget === 'single_choice' - ? ['widget', 'internal_value'] - : ['widget'], + fields: ['widget', 'display_values'], properties: { widget: { title: intl.formatMessage(messages.field_widget), @@ -43,8 +51,9 @@ export const YesNoSchemaExtender = ({ intl, formData, ...props }) => { ], default: 'checkbox', }, - internal_value: { - title: 'Internal value', + display_values: { + title: 'Display values as', + description: '', widget: 'object', schema: InternalValueSchema(), collapsible: true, From 5e97beab7d7a9b55edb36b90bf61b034c0d5016b Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Fri, 29 Sep 2023 10:04:10 +0100 Subject: [PATCH 44/51] Field schema --- src/fieldSchema.js | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/fieldSchema.js b/src/fieldSchema.js index 4b85343..014182f 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -1,6 +1,5 @@ import config from '@plone/volto/registry'; -import { defineMessages } from 'react-intl'; -import { useIntl } from 'react-intl'; +import { defineMessages, useIntl } from 'react-intl'; const messages = defineMessages({ field_label: { @@ -15,6 +14,10 @@ const messages = defineMessages({ id: 'form_field_required', defaultMessage: 'Required', }, + field_default: { + id: 'form_field_default', + defaultMessage: 'Default value', + }, field_type: { id: 'form_field_type', defaultMessage: 'Field type', @@ -95,6 +98,13 @@ const messages = defineMessages({ const choiceTypes = ['select', 'single_choice', 'multiple_choice']; +// TODO: Anyway to inrospect this? +const fieldTypeDefaultValueTypeMapping = { + yes_no: 'boolean', + multiple_choice: 'array', + date: 'date', +}; + export default (props) => { var intl = useIntl(); const baseFieldTypeChoices = [ @@ -147,6 +157,7 @@ export default (props) => { 'field_type', ...schemaExtenderValues.fields, 'required', + 'default_value', 'show_when_when', ...(props.show_when_when && props.show_when_when !== 'always' ? ['show_when_is'] @@ -182,6 +193,20 @@ export default (props) => { type: 'boolean', default: false, }, + default_value: { + title: intl.formatMessage(messages.field_default), + type: fieldTypeDefaultValueTypeMapping[props?.field_type] + ? fieldTypeDefaultValueTypeMapping[props?.field_type] + : 'string', + ...(['select', 'single_choice', 'multiple_choice'].includes( + props?.field_type, + ) && { + choices: props?.formData?.subblocks + .filter((block) => block.field_id === props.field_id)?.[0] + ?.input_values?.map((input_value) => input_value), + noValueOption: false, + }), + }, show_when_when: { title: intl.formatMessage(messages.field_show_when_when), type: 'string', From ac9f6b6b19255fb26b3227b4c332504b67f346e8 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 11 Oct 2023 18:40:27 +0100 Subject: [PATCH 45/51] WIP --- src/components/View.jsx | 2 +- src/fieldSchema.js | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/View.jsx b/src/components/View.jsx index 0509681..13b046a 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -181,7 +181,7 @@ const View = ({ data, id, path }) => { const dataToAdd = formData[fieldName] ?? { field_id: field.id, label: field.label, - value: null, + value: field.default_value, ...(data[field.id] && { custom_field_id: data[field.id] }), // Conditionally add the key. Nicer to work with than having a key with a null value }; return { ...returnValue, [fieldName]: dataToAdd }; diff --git a/src/fieldSchema.js b/src/fieldSchema.js index 014182f..930520b 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -198,12 +198,21 @@ export default (props) => { type: fieldTypeDefaultValueTypeMapping[props?.field_type] ? fieldTypeDefaultValueTypeMapping[props?.field_type] : 'string', + ...(props?.field_type === 'yes_no' && { + choices: [ + [true, 'Yes'], + [false, 'No'], + ], + noValueOption: false, + }), ...(['select', 'single_choice', 'multiple_choice'].includes( props?.field_type, ) && { choices: props?.formData?.subblocks .filter((block) => block.field_id === props.field_id)?.[0] - ?.input_values?.map((input_value) => input_value), + ?.input_values?.map((input_value) => { + return [input_value, input_value]; + }), noValueOption: false, }), }, From 25b13213092ef17a95ccc08044b4dc525e09e587 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Thu, 9 Nov 2023 22:08:48 +0000 Subject: [PATCH 46/51] Scroll to 'error box' if it can be found when an error occurs --- src/components/View.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/View.jsx b/src/components/View.jsx index 13b046a..0a82644 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -98,6 +98,7 @@ const View = ({ data, id, path }) => { const [formErrors, setFormErrors] = useState([]); const submitResults = useSelector((state) => state.submitForm); const captchaToken = useRef(); + const formid = `form-${id}`; const onChangeFormData = (field_id, field, value, extras) => { setFormData({ @@ -215,6 +216,10 @@ const View = ({ data, id, path }) => { ); setFormState({ type: FORM_STATES.loading }); } else { + const errorBox = document.getElementById(`${formid}-errors`); + if (errorBox) { + errorBox.scrollIntoView({ behavior: 'smooth' }); + } setFormState({ type: FORM_STATES.error }); } }) @@ -239,8 +244,6 @@ const View = ({ data, id, path }) => { onChangeFormData, }); - const formid = `form-${id}`; - useEffect(() => { if (submitResults?.loaded) { setFormState({ From 667d4720c777692c0c4f765eeae6784f87189314 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Fri, 10 Nov 2023 00:09:02 +0000 Subject: [PATCH 47/51] Fix radio widget required --- src/components/Widget/RadioWidget.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Widget/RadioWidget.jsx b/src/components/Widget/RadioWidget.jsx index d1b50cc..230ce12 100644 --- a/src/components/Widget/RadioWidget.jsx +++ b/src/components/Widget/RadioWidget.jsx @@ -36,7 +36,7 @@ const RadioWidget = ({ id={id} title={title} description={description} - required={required || null} + // required={required || null} error={error} fieldSet={fieldSet} wrapped={wrapped} From c389fa66616026984129dbb6b633a8fb60550d3b Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Fri, 10 Nov 2023 00:09:19 +0000 Subject: [PATCH 48/51] Fix required validation --- src/components/View.jsx | 48 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/components/View.jsx b/src/components/View.jsx index 0a82644..da6641a 100644 --- a/src/components/View.jsx +++ b/src/components/View.jsx @@ -1,13 +1,13 @@ -import React, { useState, useEffect, useReducer, useRef } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; +import { formatDate } from '@plone/volto/helpers/Utils/Date'; +import config from '@plone/volto/registry'; import PropTypes from 'prop-types'; -import { useIntl, defineMessages } from 'react-intl'; +import React, { useEffect, useReducer, useRef, useState } from 'react'; +import { defineMessages, useIntl } from 'react-intl'; +import { useDispatch, useSelector } from 'react-redux'; import { submitForm } from 'volto-form-block/actions'; -import { getFieldName } from 'volto-form-block/components/utils'; import FormView from 'volto-form-block/components/FormView'; -import { formatDate } from '@plone/volto/helpers/Utils/Date'; -import config from '@plone/volto/registry'; import { Captcha } from 'volto-form-block/components/Widget'; +import { getFieldName } from 'volto-form-block/components/utils'; const messages = defineMessages({ formSubmitted: { @@ -128,25 +128,25 @@ const View = ({ data, id, path }) => { config.blocks.blocksConfig.form.additionalFields?.filter( (f) => f.id === fieldType && f.isValid !== undefined, )?.[0] ?? null; - if ( - subblock.required && - additionalField && - !additionalField?.isValid(formData, name) - ) { - v.push(name); - } else if ( - subblock.required && - fieldType === 'checkbox' && - !formData[name]?.value - ) { - v.push(name); - } else if ( - subblock.required && - (!formData[name] || + if (subblock.required) { + let fieldIsValid = true; + if (additionalField && !additionalField?.isValid(formData, name)) { + fieldIsValid = false; + } else if (fieldType === 'checkbox' && !formData[name]?.value) { + fieldIsValid = false; + } else if ( + !formData[name] || formData[name]?.value?.length === 0 || - JSON.stringify(formData[name]?.value ?? {}) === '{}') - ) { - v.push(name); + JSON.stringify(formData[name]?.value ?? {}) === '{}' + ) { + fieldIsValid = false; + } + if (Boolean(!formData[name] && subblock.default_value)) { + fieldIsValid = true; + } + if (!fieldIsValid) { + v.push(name); + } } }); From 93d2dbf38024e6ded2579cfedd335917386d07cf Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Fri, 10 Nov 2023 20:26:56 +0000 Subject: [PATCH 49/51] Don't include default_value for attachment, static text and hidden fields --- src/fieldSchema.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/fieldSchema.js b/src/fieldSchema.js index 930520b..2642a6f 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -157,7 +157,11 @@ export default (props) => { 'field_type', ...schemaExtenderValues.fields, 'required', - 'default_value', + ...(!['attachment', 'static_text', 'hidden'].includes( + props.field_type, + ) + ? ['default_value'] + : []), 'show_when_when', ...(props.show_when_when && props.show_when_when !== 'always' ? ['show_when_is'] From 4a743272e9bcabab65441820fa2dc293bc1c776c Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Sun, 12 Nov 2023 21:58:56 +0000 Subject: [PATCH 50/51] Update title of field --- src/fieldSchema.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fieldSchema.js b/src/fieldSchema.js index 2642a6f..942682a 100644 --- a/src/fieldSchema.js +++ b/src/fieldSchema.js @@ -16,7 +16,7 @@ const messages = defineMessages({ }, field_default: { id: 'form_field_default', - defaultMessage: 'Default value', + defaultMessage: 'Default', }, field_type: { id: 'form_field_type', From 68a4d617e9b3b859eec9de94508112b463523fc1 Mon Sep 17 00:00:00 2001 From: "me@jeffersonbledsoe.com" Date: Wed, 29 Nov 2023 16:44:32 +0000 Subject: [PATCH 51/51] Fix yes/ no default values --- src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js b/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js index ad74f55..9bde393 100644 --- a/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js +++ b/src/components/FieldTypeSchemaExtenders/YesNoSchemaExtender.js @@ -29,10 +29,12 @@ function InternalValueSchema() { yes: { title: 'True', placeholder: 'Yes', + default: 'Yes', }, no: { title: 'False', placeholder: 'No', + default: 'No', }, }, };