From 9d4f69aaf8af43a4bd6b587bd6df4e48eaa7e328 Mon Sep 17 00:00:00 2001 From: "Soare Robert Daniel (Mac 2023)" Date: Tue, 29 Aug 2023 17:26:46 +0300 Subject: [PATCH 1/4] fix: allow saving in FSE for Form Options --- src/blocks/blocks/form/edit.js | 10 ++--- src/blocks/helpers/block-utility.js | 63 +++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src/blocks/blocks/form/edit.js b/src/blocks/blocks/form/edit.js index b9d029969..bd68a62ef 100644 --- a/src/blocks/blocks/form/edit.js +++ b/src/blocks/blocks/form/edit.js @@ -47,13 +47,13 @@ import { import metadata from './block.json'; import { blockInit, - getDefaultValueByField + getDefaultValueByField, + triggerSave } from '../../helpers/block-utility.js'; import Inspector from './inspector.js'; import Placeholder from './placeholder.js'; import { useResponsiveAttributes } from '../../helpers/utility-hooks'; import { renderBoxOrNumWithUnit, _cssBlock, _px, findInnerBlocks } from '../../helpers/helper-functions'; -import { Notice } from '@wordpress/components'; const { attributes: defaultAttributes } = metadata; @@ -143,17 +143,13 @@ const Edit = ({ moveBlockToPosition } = useDispatch( 'core/block-editor' ); - const { - unlockPostSaving - } = useDispatch( 'core/editor' ); - const setFormOption = option => { setFormOptions( options => ({ ...options, ...option }) ); }; const setFormOptionAndSaveUnlock = option => { setFormOption( option ); - unlockPostSaving?.(); + triggerSave(); }; const [ savedFormOptions, setSavedFormOptions ] = useState( true ); diff --git a/src/blocks/helpers/block-utility.js b/src/blocks/helpers/block-utility.js index 79e4da665..1b1c83732 100644 --- a/src/blocks/helpers/block-utility.js +++ b/src/blocks/helpers/block-utility.js @@ -537,3 +537,66 @@ export function useTabSwitch( key, defaultValue ) { return [ tab, setTab ]; } + +/** + * Get the post type and post id. + * + * @returns {{postType: string, postId: string|undefined}} + */ +export function getPostTypeAndId() { + let postId = select( 'core/editor' )?.getCurrentPostId?.(); + let postType = select( 'core/editor' )?.getCurrentPostType?.(); + + if ( ! postId ) { + + /** + * If we are in the FSE, we need to get identify the post ID and post type from the URL. + */ + const urlParams = new URLSearchParams( window.location.search ); + postId = urlParams.get( 'postId' ) ?? urlParams.get( 'post' ); + postType = urlParams.get( 'postType' ) ?? 'post'; + } + + return { postId, postType }; +} + +/** + * This timer assure us that we do not overload the server with requests with cumulative changes. + */ +let timer = null; + +/** + * Trigger the save in Editor. + */ +export function triggerSave() { + if ( timer ) { + clearTimeout( timer ); + } + + /** + * We are changing a special meta field to trigger the save. + * The Form Options are saved in the WP Options table when you press Save and not in the block attributes. + * Because of this the Editor does not know that the block has changed and it does not trigger the save. + * Thus, the Save button is keep its state. Which mean that if you do change any content in the editor, the Save button will not be enabled. + * + * Below are some small tricks to make sure that the Save button is enabled when you change the Form Options. + */ + + const { postId, postType } = getPostTypeAndId(); + + if ( ! postId ) { + return; + } + + /** + * + * A simple trick is to use a dummy meta value that is not used anywhere else. + * We set a random string to make sure the value is different every time. + * + * We use a timer to not overload the server with requests. + */ + timer = setTimeout( () => { + const randomString = Math.random().toString( 36 ).substring( 2, 15 ) + Math.random().toString( 36 ).substring( 2, 15 ); + dispatch( 'core' ).editEntityRecord( 'postType', postType, postId, { meta: { 'themeisle_blocks_mark_to_save': randomString }}); + }, 3000 ); +}; From a16411a4eacb224ef77c431d810b3b13c6ef17e7 Mon Sep 17 00:00:00 2001 From: "Soare Robert Daniel (Mac 2023)" Date: Thu, 31 Aug 2023 11:44:48 +0300 Subject: [PATCH 2/4] chore: use dummy block insertion to unlock post save --- src/blocks/blocks/form/edit.js | 18 +++++++-- src/blocks/helpers/block-utility.js | 61 ----------------------------- 2 files changed, 14 insertions(+), 65 deletions(-) diff --git a/src/blocks/blocks/form/edit.js b/src/blocks/blocks/form/edit.js index bd68a62ef..513f0a99c 100644 --- a/src/blocks/blocks/form/edit.js +++ b/src/blocks/blocks/form/edit.js @@ -3,7 +3,7 @@ */ import classnames from 'classnames'; -import { get, isEqual } from 'lodash'; +import { debounce, get, isEqual } from 'lodash'; import hash from 'object-hash'; @@ -47,8 +47,7 @@ import { import metadata from './block.json'; import { blockInit, - getDefaultValueByField, - triggerSave + getDefaultValueByField } from '../../helpers/block-utility.js'; import Inspector from './inspector.js'; import Placeholder from './placeholder.js'; @@ -147,9 +146,20 @@ const Edit = ({ setFormOptions( options => ({ ...options, ...option }) ); }; + /** + * This mark the block as dirty which allow us to use the save button to trigger the update of the form options tied to WP Options. + * + * @type {DebouncedFunc<(function(): void)|*>} + */ + const enableSaveBtn = debounce( () => { + const dummyBlock = createBlock( 'core/spacer', { height: '0px' }); + insertBlock( dummyBlock, 0, clientId, false ); + removeBlock( dummyBlock.clientId, false ); + }, 3000 ); + const setFormOptionAndSaveUnlock = option => { setFormOption( option ); - triggerSave(); + enableSaveBtn(); }; const [ savedFormOptions, setSavedFormOptions ] = useState( true ); diff --git a/src/blocks/helpers/block-utility.js b/src/blocks/helpers/block-utility.js index 1b1c83732..be1ce7d54 100644 --- a/src/blocks/helpers/block-utility.js +++ b/src/blocks/helpers/block-utility.js @@ -538,65 +538,4 @@ export function useTabSwitch( key, defaultValue ) { return [ tab, setTab ]; } -/** - * Get the post type and post id. - * - * @returns {{postType: string, postId: string|undefined}} - */ -export function getPostTypeAndId() { - let postId = select( 'core/editor' )?.getCurrentPostId?.(); - let postType = select( 'core/editor' )?.getCurrentPostType?.(); - - if ( ! postId ) { - - /** - * If we are in the FSE, we need to get identify the post ID and post type from the URL. - */ - const urlParams = new URLSearchParams( window.location.search ); - postId = urlParams.get( 'postId' ) ?? urlParams.get( 'post' ); - postType = urlParams.get( 'postType' ) ?? 'post'; - } - - return { postId, postType }; -} - -/** - * This timer assure us that we do not overload the server with requests with cumulative changes. - */ -let timer = null; - -/** - * Trigger the save in Editor. - */ -export function triggerSave() { - if ( timer ) { - clearTimeout( timer ); - } - /** - * We are changing a special meta field to trigger the save. - * The Form Options are saved in the WP Options table when you press Save and not in the block attributes. - * Because of this the Editor does not know that the block has changed and it does not trigger the save. - * Thus, the Save button is keep its state. Which mean that if you do change any content in the editor, the Save button will not be enabled. - * - * Below are some small tricks to make sure that the Save button is enabled when you change the Form Options. - */ - - const { postId, postType } = getPostTypeAndId(); - - if ( ! postId ) { - return; - } - - /** - * - * A simple trick is to use a dummy meta value that is not used anywhere else. - * We set a random string to make sure the value is different every time. - * - * We use a timer to not overload the server with requests. - */ - timer = setTimeout( () => { - const randomString = Math.random().toString( 36 ).substring( 2, 15 ) + Math.random().toString( 36 ).substring( 2, 15 ); - dispatch( 'core' ).editEntityRecord( 'postType', postType, postId, { meta: { 'themeisle_blocks_mark_to_save': randomString }}); - }, 3000 ); -}; From 143ab79a4df7663f3830d8636aa819a30fe08791 Mon Sep 17 00:00:00 2001 From: "Soare Robert Daniel (Mac 2023)" Date: Thu, 31 Aug 2023 12:13:03 +0300 Subject: [PATCH 3/4] chore: add e2e test --- src/blocks/test/e2e/blocks/form.spec.js | 45 +++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/blocks/test/e2e/blocks/form.spec.js b/src/blocks/test/e2e/blocks/form.spec.js index e8805aede..6e145b680 100644 --- a/src/blocks/test/e2e/blocks/form.spec.js +++ b/src/blocks/test/e2e/blocks/form.spec.js @@ -273,4 +273,49 @@ test.describe( 'Form Block', () => { // check for a element with the attribute data-redirect-url await expect( await page.$( `[data-redirect="${REDIRECT_URL}"]` ) ).toBeTruthy(); }); + + test( 'enable post save button on options changed', async({ page, editor }) => { + const ccValue = 'otter@test-form.com'; + + /* + * Create a form block and insert the CC value using the Inspector Controls. + */ + + await editor.insertBlock({ name: 'themeisle-blocks/form' }); + + let formBlock = ( await editor.getBlocks() ).find( ( block ) => 'themeisle-blocks/form' === block.name ); + + expect( formBlock ).toBeTruthy(); + + const { clientId } = formBlock; + + await page.click( `#block-${clientId} > div > fieldset > ul > li:nth-child(1) > button` ); + + // Open the options panel + await page.getByRole( 'button', { name: 'Form Options options' }).click(); + + // activate the option + await page.getByRole( 'menuitemcheckbox', { name: 'Show CC' }).click(); + + // Close the options panel + await page.getByRole( 'button', { name: 'Form Options options' }).click(); + + const cc = page.getByPlaceholder( 'Send copies to' ); + + await cc.fill( ccValue ); + + await editor.publishPost(); + + await page.getByLabel( 'Close panel' ).click(); + + await page.getByPlaceholder( 'Default is to admin site' ).fill( ccValue ); + + const saveBtn = page.getByRole( 'button', { name: 'Update', disabled: false }); + + await saveBtn.waitFor({ + timeout: 4000 + }); + + expect( await saveBtn.isEnabled() ).toBeTruthy(); + }); }); From e77fe2daf8ae1ef2e215f221d24caea5e9ef011b Mon Sep 17 00:00:00 2001 From: "Soare Robert Daniel (Mac 2023)" Date: Mon, 11 Sep 2023 13:11:26 +0300 Subject: [PATCH 4/4] chore: save detection for form in FSE --- src/blocks/blocks/form/edit.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/blocks/blocks/form/edit.js b/src/blocks/blocks/form/edit.js index 13ca81e47..35f25da94 100644 --- a/src/blocks/blocks/form/edit.js +++ b/src/blocks/blocks/form/edit.js @@ -106,7 +106,7 @@ const Edit = ({ * @param {import('../../common').SyncAttrs} field * @returns */ - const getSyncValue = field =>{ + const getSyncValue = field => { if ( attributes?.isSynced?.includes( field ) ) { return getDefaultValueByField({ name, field, defaultAttributes, attributes }); } @@ -210,9 +210,10 @@ const Edit = ({ const isPublishingPost = select( 'core/editor' )?.isPublishingPost(); const isAutosaving = select( 'core/editor' )?.isAutosavingPost(); const widgetSaving = select( 'core/edit-widgets' )?.isSavingWidgetAreas(); + const nonPostEntitySaving = select( 'core/editor' )?.isSavingNonPostEntityChanges(); return { - canSaveData: ( ! isAutosaving && ( isSavingPost || isPublishingPost ) ) || widgetSaving + canSaveData: ( ! isAutosaving && ( isSavingPost || isPublishingPost || nonPostEntitySaving ) ) || widgetSaving }; });