diff --git a/package-lock.json b/package-lock.json index 82ec065d65..8a1a72f537 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@fortawesome/free-solid-svg-icons": "^5.15.1", "@fortawesome/react-fontawesome": "^0.1.12", "@givewp/design-system-foundation": "^1.1.0", - "@givewp/form-builder-library": "^1.6.0", + "@givewp/form-builder-library": "^1.7.0", "@hookform/error-message": "^2.0.1", "@hookform/resolvers": "^2.9.10", "@paypal/paypal-js": "^5.1.4", @@ -2549,9 +2549,9 @@ "integrity": "sha512-SOAS98QQOytIGsyDX55y4TCS0DeKijjmOPnNaG0YbClTL2u7HFNthqRHk246BXZ0s6U+CUzqZQ8mf/+3NY4Z1g==" }, "node_modules/@givewp/form-builder-library": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@givewp/form-builder-library/-/form-builder-library-1.6.0.tgz", - "integrity": "sha512-I/ZLIFHbWSZU+PR3urCyvFR/kiSV0YZI2rBqjBT8/sYbEzh/IXNTbGdp5/1hvzpsGzVN/aThGDut71JxW0oKvA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@givewp/form-builder-library/-/form-builder-library-1.7.0.tgz", + "integrity": "sha512-7u0wbs27xizgKQCodA3ZPyuaVfX5VFYv+mknRY5x5RO4GJ/FfUQhbAr6G6t0doCKii0NePABs0l4rrCU05Q5wQ==", "dependencies": { "@wordpress/components": "^25.10.0", "@wordpress/compose": "^6.21.0", @@ -37054,9 +37054,9 @@ "integrity": "sha512-SOAS98QQOytIGsyDX55y4TCS0DeKijjmOPnNaG0YbClTL2u7HFNthqRHk246BXZ0s6U+CUzqZQ8mf/+3NY4Z1g==" }, "@givewp/form-builder-library": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@givewp/form-builder-library/-/form-builder-library-1.6.0.tgz", - "integrity": "sha512-I/ZLIFHbWSZU+PR3urCyvFR/kiSV0YZI2rBqjBT8/sYbEzh/IXNTbGdp5/1hvzpsGzVN/aThGDut71JxW0oKvA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@givewp/form-builder-library/-/form-builder-library-1.7.0.tgz", + "integrity": "sha512-7u0wbs27xizgKQCodA3ZPyuaVfX5VFYv+mknRY5x5RO4GJ/FfUQhbAr6G6t0doCKii0NePABs0l4rrCU05Q5wQ==", "requires": { "@wordpress/components": "^25.10.0", "@wordpress/compose": "^6.21.0", diff --git a/package.json b/package.json index c84655c258..de52cc064d 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "@fortawesome/free-solid-svg-icons": "^5.15.1", "@fortawesome/react-fontawesome": "^0.1.12", "@givewp/design-system-foundation": "^1.1.0", - "@givewp/form-builder-library": "^1.6.0", + "@givewp/form-builder-library": "^1.7.0", "@hookform/error-message": "^2.0.1", "@hookform/resolvers": "^2.9.10", "@paypal/paypal-js": "^5.1.4", diff --git a/src/DonationForms/Actions/ConvertDonationFormBlocksToFieldsApi.php b/src/DonationForms/Actions/ConvertDonationFormBlocksToFieldsApi.php index 34c18dd32a..4de295d50f 100644 --- a/src/DonationForms/Actions/ConvertDonationFormBlocksToFieldsApi.php +++ b/src/DonationForms/Actions/ConvertDonationFormBlocksToFieldsApi.php @@ -261,6 +261,8 @@ protected function createNodeFromBlockWithUniqueAttributes(BlockModel $block, in } /** + * @unreleased updated honorific field with validation, global options, and user defaults + * * @since 3.0.0 */ protected function createNodeFromDonorNameBlock(BlockModel $block): Node @@ -293,9 +295,17 @@ protected function createNodeFromDonorNameBlock(BlockModel $block): Node if ($block->hasAttribute('showHonorific') && $block->getAttribute('showHonorific') === true) { - $group->getNodeByName('honorific') - ->label('Title') - ->options(...array_values($block->getAttribute('honorifics'))); + $options = array_filter(array_values((array)$block->getAttribute('honorifics'))); + if ($block->hasAttribute('useGlobalSettings') && $block->getAttribute('useGlobalSettings') === true) { + $options = give_get_option('title_prefixes', give_get_default_title_prefixes()); + } + + if (!empty($options)){ + $group->getNodeByName('honorific') + ->label(__('Title', 'give')) + ->options(...$options) + ->rules('max:255', 'in:' . implode(',', $options)); + } } else { $group->remove('honorific'); } diff --git a/src/FormBuilder/ViewModels/FormBuilderViewModel.php b/src/FormBuilder/ViewModels/FormBuilderViewModel.php index 769238033b..df9924eabb 100644 --- a/src/FormBuilder/ViewModels/FormBuilderViewModel.php +++ b/src/FormBuilder/ViewModels/FormBuilderViewModel.php @@ -81,7 +81,7 @@ public function storageData(int $donationFormId): array ], 'goalTypeOptions' => $this->getGoalTypeOptions(), 'goalProgressOptions' => $this->getGoalProgressOptions(), - 'nameTitlePrefixes' => give_get_option('title_prefixes'), + 'nameTitlePrefixes' => give_get_option('title_prefixes', array_values(give_get_default_title_prefixes())), 'isExcerptEnabled' => give_is_setting_enabled(give_get_option('forms_excerpt')), 'intlTelInputSettings' => IntlTelInput::getSettings(), ]; diff --git a/src/FormBuilder/resources/js/form-builder/src/blocks/fields/donor-name/Edit.tsx b/src/FormBuilder/resources/js/form-builder/src/blocks/fields/donor-name/Edit.tsx index 159f907397..4db4f5f7d3 100644 --- a/src/FormBuilder/resources/js/form-builder/src/blocks/fields/donor-name/Edit.tsx +++ b/src/FormBuilder/resources/js/form-builder/src/blocks/fields/donor-name/Edit.tsx @@ -2,16 +2,48 @@ import {__} from '@wordpress/i18n'; import {BlockEditProps} from '@wordpress/blocks'; import {PanelBody, PanelRow, SelectControl, TextControl, ToggleControl} from '@wordpress/components'; import {InspectorControls} from '@wordpress/block-editor'; -import {useEffect, useState} from 'react'; +import {useState} from 'react'; import {OptionsPanel} from '@givewp/form-builder-library'; import type {OptionProps} from '@givewp/form-builder-library/build/OptionsPanel/types'; import {getFormBuilderWindowData} from '@givewp/form-builder/common/getWindowData'; const titleLabelTransform = (token = '') => token.charAt(0).toUpperCase() + token.slice(1); const titleValueTransform = (token = '') => token.trim().toLowerCase(); +const convertHonorificsToOptions = (honorifics: string[], defaultValue?: string) => + honorifics?.filter(label => label.length > 0).map((honorific: string) => ({ + label: titleLabelTransform(honorific), + value: titleValueTransform(honorific), + checked: defaultValue ? defaultValue === honorific : honorifics[0] === honorific + }) as OptionProps + ); + +const convertOptionsToHonorifics = (options: OptionProps[]) => { + const honorifics = []; + Object.values(options).forEach((option) => { + if (option.label.length > 0) { + honorifics.push(option.label); + } + }); + + return honorifics; +} + +type Attributes = { + showHonorific: boolean; + useGlobalSettings: boolean; + honorifics: string[]; + firstNameLabel: string; + firstNamePlaceholder: string; + lastNameLabel: string; + lastNamePlaceholder: string; + requireLastName: boolean; +} export default function Edit({ - attributes: { + attributes, + setAttributes + }: BlockEditProps) { + const { showHonorific, useGlobalSettings, honorifics, @@ -19,67 +51,40 @@ export default function Edit({ firstNamePlaceholder, lastNameLabel, lastNamePlaceholder, - requireLastName, - }, - setAttributes, -}: BlockEditProps) { + requireLastName + } = attributes as Attributes; + const globalHonorifics = getFormBuilderWindowData().nameTitlePrefixes; const [selectedTitle, setSelectedTitle] = useState((Object.values(honorifics)[0] as string) ?? ''); const [honorificOptions, setHonorificOptions] = useState( - Object.values(honorifics).map((token: string) => { - return { - label: titleLabelTransform(token), - value: titleValueTransform(token), - checked: selectedTitle === token, - } as OptionProps; - }) + convertHonorificsToOptions(Object.values(honorifics), selectedTitle) ); const setOptions = (options: OptionProps[]) => { setHonorificOptions(options); - const filtered = {}; - // Filter options - Object.values(options).forEach((option) => { - Object.assign(filtered, {[option.label]: option.label}); - }); - - setAttributes({honorifics: filtered}); + setAttributes({ honorifics: convertOptionsToHonorifics(options) }); }; if (typeof useGlobalSettings === 'undefined') { - setAttributes({useGlobalSettings: true}); + setAttributes({ useGlobalSettings: true }); } - useEffect(() => { - const options = !!useGlobalSettings ? getFormBuilderWindowData().nameTitlePrefixes : ['Mr', 'Ms', 'Mrs']; - - setOptions( - Object.values(options).map((token: string) => { - return { - label: titleLabelTransform(token), - value: titleValueTransform(token), - checked: selectedTitle === token, - } as OptionProps; - }) - ); - }, [useGlobalSettings]); - return ( <>
0 ? '1fr 2fr 2fr' : '1fr 1fr', - gap: '15px', + gap: '15px' }} > {!!showHonorific && ( )} setAttributes({showHonorific: !showHonorific})} + onChange={() => setAttributes({ showHonorific: !showHonorific })} help={__( - "Do you want to add a name title prefix dropdown field before the donor's first name field? This will display a dropdown with options such as Mrs, Miss, Ms, Sir, and Dr for the donor to choose from.", + 'Do you want to add a name title prefix dropdown field before the donor\'s first name field? This will display a dropdown with options such as Mrs, Miss, Ms, Sir, and Dr for the donor to choose from.', 'give' )} /> {!!showHonorific && ( -
+
setAttributes({useGlobalSettings: !useGlobalSettings})} - value={useGlobalSettings} + onChange={() => setAttributes({ useGlobalSettings: !useGlobalSettings })} + value={useGlobalSettings ? 'true' : 'false'} options={[ - {label: __('Global', 'give'), value: 'true'}, - {label: __('Customize', 'give'), value: 'false'}, + { label: __('Global', 'give'), value: 'true' }, + { label: __('Customize', 'give'), value: 'false' } ]} />
@@ -137,7 +142,7 @@ export default function Edit({ fontSize: '0.75rem', lineHeight: '120%', fontWeight: 400, - marginTop: '0.5rem', + marginTop: '0.5rem' }} > {__(' Go to the settings to change the ')} @@ -155,12 +160,13 @@ export default function Edit({ )} {!!showHonorific && !useGlobalSettings && ( -
+
@@ -171,14 +177,14 @@ export default function Edit({ setAttributes({firstNameLabel: value})} + onChange={(value) => setAttributes({ firstNameLabel: value })} /> setAttributes({firstNamePlaceholder: value})} + onChange={(value) => setAttributes({ firstNamePlaceholder: value })} /> @@ -187,21 +193,21 @@ export default function Edit({ setAttributes({lastNameLabel: value})} + onChange={(value) => setAttributes({ lastNameLabel: value })} /> setAttributes({lastNamePlaceholder: value})} + onChange={(value) => setAttributes({ lastNamePlaceholder: value })} /> setAttributes({requireLastName: !requireLastName})} + onChange={() => setAttributes({ requireLastName: !requireLastName })} help={__('Do you want to force the Last Name field to be required?', 'give')} /> diff --git a/src/FormBuilder/resources/js/form-builder/src/blocks/fields/donor-name/settings.tsx b/src/FormBuilder/resources/js/form-builder/src/blocks/fields/donor-name/settings.tsx index a5fcbcce57..3add6edfb2 100644 --- a/src/FormBuilder/resources/js/form-builder/src/blocks/fields/donor-name/settings.tsx +++ b/src/FormBuilder/resources/js/form-builder/src/blocks/fields/donor-name/settings.tsx @@ -23,7 +23,7 @@ const settings: FieldBlock['settings'] = { }, honorifics: { type: 'array', - default: ['Mr', 'Ms', 'Mrs'], + default: [__('Mr.', 'give'), __('Ms.', 'give'), __('Mrs.', 'give')], }, firstNameLabel: { type: 'string', diff --git a/tests/Unit/ViewModels/FormBuilderViewModelTest.php b/tests/Unit/ViewModels/FormBuilderViewModelTest.php index 34895f2fc7..2ef60a3b02 100644 --- a/tests/Unit/ViewModels/FormBuilderViewModelTest.php +++ b/tests/Unit/ViewModels/FormBuilderViewModelTest.php @@ -88,7 +88,7 @@ public function testShouldReturnStorageData() ], 'goalTypeOptions' => $viewModel->getGoalTypeOptions(), 'goalProgressOptions' => $viewModel->getGoalProgressOptions(), - 'nameTitlePrefixes' => give_get_option('title_prefixes'), + 'nameTitlePrefixes' => give_get_option('title_prefixes', array_values(give_get_default_title_prefixes())), 'isExcerptEnabled' => give_is_setting_enabled(give_get_option('forms_excerpt')), 'intlTelInputSettings' => IntlTelInput::getSettings(), ],