diff --git a/src/blocks/blocks/accordion/group/inspector.js b/src/blocks/blocks/accordion/group/inspector.js index 93c93062b..4029e35c0 100644 --- a/src/blocks/blocks/accordion/group/inspector.js +++ b/src/blocks/blocks/accordion/group/inspector.js @@ -49,6 +49,7 @@ import { } from '../../../helpers/helper-functions.js'; import { useResponsiveAttributes } from '../../../helpers/utility-hooks.js'; +import { useTabSwitch } from '../../../helpers/block-utility'; const styles = [ { @@ -79,7 +80,7 @@ const Inspector = ({ setAttributes, getValue }) => { - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); const globalColorControls = [ { diff --git a/src/blocks/blocks/advanced-heading/inspector.js b/src/blocks/blocks/advanced-heading/inspector.js index 03c387cbf..a7707864d 100644 --- a/src/blocks/blocks/advanced-heading/inspector.js +++ b/src/blocks/blocks/advanced-heading/inspector.js @@ -54,6 +54,7 @@ import { import { useResponsiveAttributes } from '../../helpers/utility-hooks.js'; import { makeBox } from '../../plugins/copy-paste/utils'; import { _px } from '../../helpers/helper-functions.js'; +import { useTabSwitch } from '../../helpers/block-utility'; /** * @@ -65,7 +66,7 @@ const Inspector = ({ setAttributes }) => { - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); const { responsiveSetAttributes, responsiveGetAttributes } = useResponsiveAttributes( setAttributes ); const changeFontFamily = value => { diff --git a/src/blocks/blocks/button-group/button/inspector.js b/src/blocks/blocks/button-group/button/inspector.js index 23f61524a..6d7520731 100644 --- a/src/blocks/blocks/button-group/button/inspector.js +++ b/src/blocks/blocks/button-group/button/inspector.js @@ -33,6 +33,7 @@ import { ColorDropdownControl, InspectorHeader, SyncControlDropdown, ToogleGroup import { changeActiveStyle, getActiveStyle, objectOrNumberAsBox } from '../../../helpers/helper-functions.js'; import AutoDisableSyncAttr from '../../../components/auto-disable-sync-attr/index'; import { uniq } from 'lodash'; +import { useTabSwitch } from '../../../helpers/block-utility'; const styles = [ { @@ -150,7 +151,7 @@ const Inspector = ({ ); }; - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); return ( diff --git a/src/blocks/blocks/button-group/group/inspector.js b/src/blocks/blocks/button-group/group/inspector.js index c56bad278..926d0636d 100644 --- a/src/blocks/blocks/button-group/group/inspector.js +++ b/src/blocks/blocks/button-group/group/inspector.js @@ -37,6 +37,7 @@ import { InspectorHeader, SyncControlDropdown } from '../../../components/index. import { getChoice, _i, _px } from '../../../helpers/helper-functions.js'; import TypographySelectorControl from '../../../components/typography-selector-control/index'; import AutoDisableSyncAttr from '../../../components/auto-disable-sync-attr'; +import { useTabSwitch } from '../../../helpers/block-utility'; /** * @@ -116,7 +117,7 @@ const Inspector = ({ }; }; - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); const [ proxyStore, setProxyStore ] = useState({ padding: makeBoxFromSplitAxis( diff --git a/src/blocks/blocks/countdown/inspector.js b/src/blocks/blocks/countdown/inspector.js index aaa6c0f9a..089badc9a 100644 --- a/src/blocks/blocks/countdown/inspector.js +++ b/src/blocks/blocks/countdown/inspector.js @@ -48,6 +48,7 @@ import { import { mergeBoxDefaultValues, removeBoxDefaultValues, setUtm } from '../../helpers/helper-functions.js'; import { useResponsiveAttributes } from '../../helpers/utility-hooks.js'; +import { useTabSwitch } from '../../helpers/block-utility'; const defaultFontSizes = [ { @@ -174,7 +175,7 @@ const Inspector = ({ } }; - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); const settings = __experimentalGetSettings(); diff --git a/src/blocks/blocks/flip/inspector.js b/src/blocks/blocks/flip/inspector.js index 0c94b1b88..91ea01701 100644 --- a/src/blocks/blocks/flip/inspector.js +++ b/src/blocks/blocks/flip/inspector.js @@ -67,6 +67,7 @@ import { import { alignBottom, alignTop, alignCenter as oAlignCenter } from '../../helpers/icons.js'; import { useResponsiveAttributes } from '../../helpers/utility-hooks.js'; +import { useTabSwitch } from '../../helpers/block-utility'; const defaultFontSizes = [ { @@ -102,7 +103,7 @@ const Inspector = ({ currentSide, setSide }) => { - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); const { responsiveSetAttributes, diff --git a/src/blocks/blocks/form/inspector.js b/src/blocks/blocks/form/inspector.js index 3cdcb30ec..a9944139a 100644 --- a/src/blocks/blocks/form/inspector.js +++ b/src/blocks/blocks/form/inspector.js @@ -66,6 +66,7 @@ import { _px, setUtm } from '../../helpers/helper-functions.js'; import { SortableInputField } from './sortable-input-fields'; import AutoDisableSyncAttr from '../../components/auto-disable-sync-attr/index'; import { selectAllFieldsFromForm } from './common'; +import { useTabSwitch } from '../../helpers/block-utility'; const compare = x => { return x?.[1] && x[0] !== x[1]; @@ -310,7 +311,7 @@ const Inspector = ({ setAttributes }) => { - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); const [ buttonColorView, setButtonColorView ] = useState( 'normal' ); const { diff --git a/src/blocks/blocks/google-map/inspector.js b/src/blocks/blocks/google-map/inspector.js index a2f82b24b..053c0ea9b 100644 --- a/src/blocks/blocks/google-map/inspector.js +++ b/src/blocks/blocks/google-map/inspector.js @@ -38,6 +38,7 @@ import { import { useResponsiveAttributes } from '../../helpers/utility-hooks.js'; import MarkerWrapper from './components/marker-wrapper.js'; +import { useTabSwitch } from '../../helpers/block-utility'; const px = value => value ? `${ value }px` : value; @@ -64,7 +65,7 @@ const Inspector = ({ changeAPI, saveAPIKey }) => { - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); const { responsiveSetAttributes, diff --git a/src/blocks/blocks/icon-list/inspector.js b/src/blocks/blocks/icon-list/inspector.js index fb1468827..3062bc6d2 100644 --- a/src/blocks/blocks/icon-list/inspector.js +++ b/src/blocks/blocks/icon-list/inspector.js @@ -40,6 +40,7 @@ import { import { _px } from '../../helpers/helper-functions.js'; import { useResponsiveAttributes } from '../../helpers/utility-hooks.js'; +import { useTabSwitch } from '../../helpers/block-utility'; /** * @@ -50,7 +51,7 @@ const Inspector = ({ attributes, setAttributes }) => { - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); const { responsiveSetAttributes, responsiveGetAttributes } = useResponsiveAttributes( setAttributes ); diff --git a/src/blocks/blocks/popup/inspector.js b/src/blocks/blocks/popup/inspector.js index 9285b3628..42e56c1da 100644 --- a/src/blocks/blocks/popup/inspector.js +++ b/src/blocks/blocks/popup/inspector.js @@ -37,6 +37,7 @@ import { import { removeBoxDefaultValues, setUtm } from '../../helpers/helper-functions.js'; import { useResponsiveAttributes } from '../../helpers/utility-hooks.js'; +import { useTabSwitch } from '../../helpers/block-utility'; /** * @@ -79,7 +80,7 @@ const Inspector = ({ attributes, setAttributes }) => { - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); const { responsiveSetAttributes, diff --git a/src/blocks/blocks/posts/inspector.js b/src/blocks/blocks/posts/inspector.js index 866869c07..56b34dfaf 100644 --- a/src/blocks/blocks/posts/inspector.js +++ b/src/blocks/blocks/posts/inspector.js @@ -49,6 +49,7 @@ import { numberToBox } from '../../helpers/helper-functions.js'; import { useResponsiveAttributes } from '../../helpers/utility-hooks.js'; +import { useTabSwitch } from '../../helpers/block-utility'; const styles = [ { @@ -101,7 +102,7 @@ const Inspector = ({ categoriesList, isLoading }) => { - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); const { slugs diff --git a/src/blocks/blocks/review/inspector.js b/src/blocks/blocks/review/inspector.js index 0b5c2f7ad..14d423e74 100644 --- a/src/blocks/blocks/review/inspector.js +++ b/src/blocks/blocks/review/inspector.js @@ -54,6 +54,7 @@ import { setUtm } from '../../helpers/helper-functions.js'; import { useResponsiveAttributes } from '../../helpers/utility-hooks.js'; +import { useTabSwitch } from '../../helpers/block-utility'; /** * Block Styles @@ -181,7 +182,7 @@ const Inspector = ({ getValue, productAttributes }) => { - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); const { responsiveSetAttributes, diff --git a/src/blocks/blocks/section/column/inspector.js b/src/blocks/blocks/section/column/inspector.js index ba425cfef..acc6c00b4 100644 --- a/src/blocks/blocks/section/column/inspector.js +++ b/src/blocks/blocks/section/column/inspector.js @@ -49,6 +49,7 @@ import { isNullObject, removeBoxDefaultValues } from '../../../helpers/helper-functions.js'; +import { useTabSwitch } from '../../../helpers/block-utility'; /** * @@ -72,7 +73,7 @@ const Inspector = ({ return __experimentalGetPreviewDeviceType ? __experimentalGetPreviewDeviceType() : getView(); }, []); - const [ tab, setTab ] = useState( 'layout' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'layout' ); const changeColumnWidth = value => { const width = value || 10; diff --git a/src/blocks/blocks/section/columns/inspector.js b/src/blocks/blocks/section/columns/inspector.js index 6a1546e9b..0da7a788c 100644 --- a/src/blocks/blocks/section/columns/inspector.js +++ b/src/blocks/blocks/section/columns/inspector.js @@ -58,6 +58,7 @@ import { } from '../../../components/index.js'; import { useResponsiveAttributes } from '../../../helpers/utility-hooks.js'; +import { useTabSwitch } from '../../../helpers/block-utility'; /** * @@ -82,7 +83,7 @@ const Inspector = ({ const { responsiveSetAttributes } = useResponsiveAttributes( setAttributes ); - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); const changeColumns = value => { if ( 6 >= value ) { diff --git a/src/blocks/blocks/slider/inspector.js b/src/blocks/blocks/slider/inspector.js index e8320d313..65409eee3 100644 --- a/src/blocks/blocks/slider/inspector.js +++ b/src/blocks/blocks/slider/inspector.js @@ -40,6 +40,7 @@ import { import { useResponsiveAttributes } from '../../helpers/utility-hooks.js'; import { _px } from '../../helpers/helper-functions.js'; import { getMaxPerView } from './edit.js'; +import { useTabSwitch } from '../../helpers/block-utility'; /** * @@ -52,7 +53,7 @@ const Inspector = ({ onSelectImages, changePerView }) => { - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); const { responsiveSetAttributes, diff --git a/src/blocks/blocks/tabs/group/inspector.js b/src/blocks/blocks/tabs/group/inspector.js index 2c7f84b29..4be5f42e8 100644 --- a/src/blocks/blocks/tabs/group/inspector.js +++ b/src/blocks/blocks/tabs/group/inspector.js @@ -34,6 +34,7 @@ import { alignCenter, alignLeft, alignRight, menu } from '@wordpress/icons'; import { changeActiveStyle, getActiveStyle, isEmptyBox, objectOrNumberAsBox } from '../../../helpers/helper-functions.js'; import AutoDisableSyncAttr from '../../../components/auto-disable-sync-attr/index'; import { makeBox } from '../../../plugins/copy-paste/utils'; +import { useTabSwitch } from '../../../helpers/block-utility'; const styles = [ { @@ -120,7 +121,7 @@ const Inspector = ({ return { label: `${ index + 1 }. ${ c.attributes.title || __( 'Untitled Tab', 'otter-blocks' ) }`, value: c.clientId }; }); - const [ tab, setTab ] = useState( 'settings' ); + const [ tab, setTab ] = useTabSwitch( attributes.id, 'settings' ); return ( diff --git a/src/blocks/helpers/block-utility.js b/src/blocks/helpers/block-utility.js index c0b7bf889..79e4da665 100644 --- a/src/blocks/helpers/block-utility.js +++ b/src/blocks/helpers/block-utility.js @@ -476,3 +476,64 @@ export function pullReusableBlockContentById( id ) { export function openOtterSidebarMenu() { document?.querySelector( '.interface-pinned-items button[aria-label~="Otter"]' )?.click(); } + +export class GlobalStateMemory { + constructor() { + this.states = {}; + window.addEventListener( 'message', this.handleMessage.bind( this ) ); + } + + /** + * Handle the message event. + * + * @param {MessageEvent} event The message event. + */ + handleMessage( event ) { + if ( 'object' === typeof event.data && null !== event.data && 'otterMemoryState' in event.data ) { + const { key, value, location } = event.data.otterMemoryState; + if ( this.states[location] === undefined ) { + this.states[location] = {}; + } + + this.states[location][key] = value; + } + } + + /** + * Get the state value. + * @param {string} location The location of the state. + * @param {string} key The key of the state. + * @returns {undefined|*} + */ + getState( location, key ) { + if ( this.states[location] === undefined ) { + return undefined; + } + return this.states[location][key]; + } +} + +/** + * The global state memory. + * + * @param {string} key The key of the state. + * @param {any} defaultValue The default value of the state. + * @returns {unknown[]} + */ +export function useTabSwitch( key, defaultValue ) { + const location = 'tab'; + const [ tab, setTab ] = useState( ( window?.parent ?? window ).otterStateMemory.getState( location, key ) ?? defaultValue ); + + useEffect( () => { + ( window.parent !== undefined ? window?.parent : window ) + .postMessage?.({ + otterMemoryState: { + key, + location, + value: tab + } + }); + }, [ tab ]); + + return [ tab, setTab ]; +} diff --git a/src/blocks/index.js b/src/blocks/index.js index 4eab2377c..41f66b3b2 100644 --- a/src/blocks/index.js +++ b/src/blocks/index.js @@ -24,6 +24,7 @@ import { otterIconColored as icon } from './helpers/icons.js'; import { setUtm } from './helpers/helper-functions.js'; +import { GlobalStateMemory } from './helpers/block-utility'; updateCategory( 'themeisle-blocks', { icon }); updateCategory( 'themeisle-woocommerce-blocks', { icon }); @@ -115,3 +116,5 @@ domReady( () => { gradient ); }); + +window.otterStateMemory = new GlobalStateMemory(); diff --git a/src/blocks/test/e2e/blocks/global-memory.spec.js b/src/blocks/test/e2e/blocks/global-memory.spec.js new file mode 100644 index 000000000..63c7c3858 --- /dev/null +++ b/src/blocks/test/e2e/blocks/global-memory.spec.js @@ -0,0 +1,36 @@ +/** + * WordPress dependencies + */ +import { test, expect } from '@wordpress/e2e-test-utils-playwright'; + +test.describe( 'Global Memory State', () => { + test.beforeEach( async({ admin }) => { + await admin.createNewPost(); + }); + + test( 'keep tab state between Desktop, Table & Mobile', async({ editor, page }) => { + + await editor.insertBlock({ + name: 'themeisle-blocks/tabs' + }); + + const styleDesktop = page.getByRole( 'button', { name: 'Style', exact: true }); + + await styleDesktop.click(); + + await page.getByRole( 'button', { name: 'Preview' }).click(); + await page.getByRole( 'menuitem', { name: 'Tablet' }).click(); + + const styleTablet = page.getByRole( 'button', { name: 'Style', exact: true }); + await styleTablet.waitFor(); + + expect( await styleTablet.isVisible() ).toBeTruthy(); + + await page.getByRole( 'menuitem', { name: 'Desktop' }).click(); + + const styleDesktopAfter = page.getByRole( 'button', { name: 'Style', exact: true }); + await styleDesktopAfter.waitFor(); + + expect( await styleDesktopAfter.isVisible() ).toBeTruthy(); + }); +});