diff --git a/packages/js/product-editor/changelog/update-product-editor-move-modal-editor-to-editor-level b/packages/js/product-editor/changelog/update-product-editor-move-modal-editor-to-editor-level new file mode 100644 index 00000000000..b6b10e0e445 --- /dev/null +++ b/packages/js/product-editor/changelog/update-product-editor-move-modal-editor-to-editor-level @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +[Product Block Editor]: move Modal edittor out of the description block edit component diff --git a/packages/js/product-editor/src/blocks/product-fields/description/edit.tsx b/packages/js/product-editor/src/blocks/product-fields/description/edit.tsx index 4e2e1078000..371dc3a1f0c 100644 --- a/packages/js/product-editor/src/blocks/product-fields/description/edit.tsx +++ b/packages/js/product-editor/src/blocks/product-fields/description/edit.tsx @@ -2,7 +2,7 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { createElement } from '@wordpress/element'; +import { createElement, useEffect } from '@wordpress/element'; import { BlockAttributes, BlockInstance, @@ -19,10 +19,9 @@ import { useEntityProp } from '@wordpress/core-data'; * Internal dependencies */ import { ContentPreview } from '../../../components/content-preview'; -import { ModalEditor } from '../../../components/modal-editor'; import { ProductEditorBlockEditProps } from '../../../types'; import ModalEditorWelcomeGuide from '../../../components/modal-editor-welcome-guide'; -import { store as productEditorUiStore } from '../../../store/product-editor-ui'; +import { store } from '../../../store/product-editor-ui'; /** * Internal dependencies @@ -58,19 +57,43 @@ export function DescriptionBlockEdit( { 'description' ); - // Check if the Modal editor is open from the store. - const isModalEditorOpen = useSelect( ( select ) => { - return select( productEditorUiStore ).isModalEditorOpen(); - }, [] ); + // Pick Modal editor data from the store. + const { isModalEditorOpen, modalEditorBlocks, hasChanged } = useSelect( + ( select ) => { + return { + isModalEditorOpen: select( store ).isModalEditorOpen(), + modalEditorBlocks: select( store ).getModalEditorBlocks(), + hasChanged: select( store ).getModalEditorContentHasChanged(), + }; + }, + [] + ); - const { openModalEditor, closeModalEditor } = - useDispatch( productEditorUiStore ); + const { openModalEditor, setModalEditorBlocks } = useDispatch( store ); + + // Update the description when the blocks change. + useEffect( () => { + if ( ! hasChanged ) { + return; + } + + if ( ! modalEditorBlocks?.length ) { + setDescription( '' ); + } + + const html = serialize( clearDescriptionIfEmpty( modalEditorBlocks ) ); + setDescription( html ); + }, [ modalEditorBlocks, setDescription, hasChanged ] ); return (
- { isModalEditorOpen && ( - { - const html = serialize( - clearDescriptionIfEmpty( blocks ) - ); - setDescription( html ); - } } - onClose={ closeModalEditor } - title={ __( 'Edit description', 'woocommerce' ) } - /> - ) } - { !! description.length && ( ) } diff --git a/packages/js/product-editor/src/components/block-editor/block-editor.tsx b/packages/js/product-editor/src/components/block-editor/block-editor.tsx index 0fe3684dea6..5cba768cbde 100644 --- a/packages/js/product-editor/src/components/block-editor/block-editor.tsx +++ b/packages/js/product-editor/src/components/block-editor/block-editor.tsx @@ -6,6 +6,7 @@ import { createElement, useMemo, useLayoutEffect } from '@wordpress/element'; import { useDispatch, useSelect, select as WPSelect } from '@wordpress/data'; import { uploadMedia } from '@wordpress/media-utils'; import { PluginArea } from '@wordpress/plugins'; +import { __ } from '@wordpress/i18n'; import { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore No types for this exist yet. @@ -34,6 +35,8 @@ import { import { useConfirmUnsavedProductChanges } from '../../hooks/use-confirm-unsaved-product-changes'; import { ProductEditorContext } from '../../types'; import { PostTypeContext } from '../../contexts/post-type-context'; +import { ModalEditor } from '../modal-editor'; +import { store as productEditorUiStore } from '../../store/product-editor-ui'; type BlockEditorSettings = Partial< EditorSettings & EditorBlockListSettings @@ -111,10 +114,26 @@ export function BlockEditor( { updateEditorSettings( settings ?? {} ); }, [ productType, productId ] ); + // Check if the Modal editor is open from the store. + const isModalEditorOpen = useSelect( ( select ) => { + return select( productEditorUiStore ).isModalEditorOpen(); + }, [] ); + + const { closeModalEditor } = useDispatch( productEditorUiStore ); + if ( ! blocks ) { return null; } + if ( isModalEditorOpen ) { + return ( + + ); + } + return (
@@ -123,6 +142,7 @@ export function BlockEditor( { onInput={ onInput } onChange={ onChange } settings={ settings } + useSubRegistry={ false } > { /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ } { /* @ts-ignore No types for this exist yet. */ } diff --git a/packages/js/product-editor/src/components/iframe-editor/iframe-editor.tsx b/packages/js/product-editor/src/components/iframe-editor/iframe-editor.tsx index 0b9a5051c4c..2e68d8b8215 100644 --- a/packages/js/product-editor/src/components/iframe-editor/iframe-editor.tsx +++ b/packages/js/product-editor/src/components/iframe-editor/iframe-editor.tsx @@ -31,31 +31,41 @@ import { HeaderToolbar } from './header-toolbar/header-toolbar'; import { ResizableEditor } from './resizable-editor'; import { SecondarySidebar } from './secondary-sidebar/secondary-sidebar'; import { useEditorHistory } from './hooks/use-editor-history'; +import { store as productEditorUiStore } from '../../store/product-editor-ui'; type IframeEditorProps = { - closeModal?: () => void; initialBlocks?: BlockInstance[]; onChange?: ( blocks: BlockInstance[] ) => void; onClose?: () => void; onInput?: ( blocks: BlockInstance[] ) => void; settings?: Partial< EditorSettings & EditorBlockListSettings > | undefined; + showBackButton?: boolean; }; export function IframeEditor( { - closeModal = () => {}, initialBlocks = [], onChange = () => {}, onClose, onInput = () => {}, settings: __settings, + showBackButton = false, }: IframeEditorProps ) { const [ resizeObserver ] = useResizeObserver(); - const [ blocks, setBlocks ] = useState< BlockInstance[] >( initialBlocks ); const [ temporalBlocks, setTemporalBlocks ] = useState< BlockInstance[] >( initialBlocks ); + + // Pick the blocks from the store. + const blocks: BlockInstance[] = useSelect( ( select ) => { + return select( productEditorUiStore ).getModalEditorBlocks(); + }, [] ); + + const { setModalEditorBlocks: setBlocks, setModalEditorContentHasChanged } = + useDispatch( productEditorUiStore ); + const { appendEdit } = useEditorHistory( { setBlocks, } ); + const { appendEdit: tempAppendEdit, hasRedo, @@ -127,15 +137,16 @@ export function IframeEditor( { onSave={ () => { appendEdit( temporalBlocks ); setBlocks( temporalBlocks ); + setModalEditorContentHasChanged( true ); onChange( temporalBlocks ); - closeModal(); + onClose?.(); } } onCancel={ () => { appendEdit( blocks ); setBlocks( blocks ); onChange( blocks ); setTemporalBlocks( blocks ); - closeModal(); + onClose?.(); } } />
@@ -157,7 +168,7 @@ export function IframeEditor( { { /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ } { /* @ts-ignore */ } - { onClose && ( + { showBackButton && onClose && ( { setTimeout( onClose, 550 ); diff --git a/packages/js/product-editor/src/components/modal-editor/modal-editor.tsx b/packages/js/product-editor/src/components/modal-editor/modal-editor.tsx index 56bb0b8b0e3..a6d36c39267 100644 --- a/packages/js/product-editor/src/components/modal-editor/modal-editor.tsx +++ b/packages/js/product-editor/src/components/modal-editor/modal-editor.tsx @@ -3,6 +3,7 @@ */ import { BlockInstance } from '@wordpress/blocks'; import { createElement } from '@wordpress/element'; +import { useDispatch } from '@wordpress/data'; import { EditorSettings, EditorBlockListSettings, @@ -14,11 +15,12 @@ import { useDebounce } from '@wordpress/compose'; * Internal dependencies */ import { IframeEditor } from '../iframe-editor'; +import { store as productEditorUiStore } from '../../store/product-editor-ui'; type ModalEditorProps = { initialBlocks?: BlockInstance[]; - onChange: ( blocks: BlockInstance[] ) => void; - onClose: () => void; + onChange?: ( blocks: BlockInstance[] ) => void; + onClose?: () => void; settings?: Partial< EditorSettings & EditorBlockListSettings > | undefined; title: string; }; @@ -29,16 +31,19 @@ export function ModalEditor( { onClose, title, }: ModalEditorProps ) { + const { closeModalEditor } = useDispatch( productEditorUiStore ); + const debouncedOnChange = useDebounce( ( blocks: BlockInstance[] ) => { - onChange( blocks ); + onChange?.( blocks ); }, 250 ); function handleClose() { const blocks = debouncedOnChange.flush(); if ( blocks ) { - onChange( blocks ); + onChange?.( blocks ); } - onClose(); + closeModalEditor(); + onClose?.(); } return ( @@ -52,7 +57,7 @@ export function ModalEditor( { initialBlocks={ initialBlocks } onInput={ debouncedOnChange } onChange={ debouncedOnChange } - closeModal={ handleClose } + onClose={ handleClose } /> ); diff --git a/packages/js/product-editor/src/store/product-editor-ui/actions.ts b/packages/js/product-editor/src/store/product-editor-ui/actions.ts index 3e1c44fe03e..eb9122e0e6b 100644 --- a/packages/js/product-editor/src/store/product-editor-ui/actions.ts +++ b/packages/js/product-editor/src/store/product-editor-ui/actions.ts @@ -1,18 +1,36 @@ +/** + * External dependencies + */ +import { BlockInstance } from '@wordpress/blocks'; + /** * Internal dependencies */ import { ACTION_MODAL_EDITOR_CLOSE, ACTION_MODAL_EDITOR_OPEN, + ACTION_MODAL_EDITOR_SET_BLOCKS, + ACTION_MODAL_EDITOR_CONTENT_HAS_CHANGED, } from './constants'; const modalEditorActions = { openModalEditor: () => ( { type: ACTION_MODAL_EDITOR_OPEN, } ), + closeModalEditor: () => ( { type: ACTION_MODAL_EDITOR_CLOSE, } ), + + setModalEditorBlocks: ( blocks: BlockInstance ) => ( { + type: ACTION_MODAL_EDITOR_SET_BLOCKS, + blocks, + } ), + + setModalEditorContentHasChanged: ( hasChanged: boolean ) => ( { + type: ACTION_MODAL_EDITOR_CONTENT_HAS_CHANGED, + hasChanged, + } ), }; export default { diff --git a/packages/js/product-editor/src/store/product-editor-ui/constants.ts b/packages/js/product-editor/src/store/product-editor-ui/constants.ts index 5fe6baa0ee0..0cba515d643 100644 --- a/packages/js/product-editor/src/store/product-editor-ui/constants.ts +++ b/packages/js/product-editor/src/store/product-editor-ui/constants.ts @@ -1,5 +1,8 @@ /** * Full editor actions */ -export const ACTION_MODAL_EDITOR_OPEN = 'MODAL_EDITOROPEN'; -export const ACTION_MODAL_EDITOR_CLOSE = 'MODAL_EDITORCLOSE'; +export const ACTION_MODAL_EDITOR_OPEN = 'MODAL_EDITOR_OPEN'; +export const ACTION_MODAL_EDITOR_CLOSE = 'MODAL_EDITOR_CLOSE'; +export const ACTION_MODAL_EDITOR_SET_BLOCKS = 'MODAL_EDITOR_SET_BLOCKS'; +export const ACTION_MODAL_EDITOR_CONTENT_HAS_CHANGED = + 'MODAL_EDITOR_CONTENT_HAS_CHANGED'; diff --git a/packages/js/product-editor/src/store/product-editor-ui/reducer.ts b/packages/js/product-editor/src/store/product-editor-ui/reducer.ts index 8f48c3fb27f..8ad8ed38787 100644 --- a/packages/js/product-editor/src/store/product-editor-ui/reducer.ts +++ b/packages/js/product-editor/src/store/product-editor-ui/reducer.ts @@ -2,8 +2,10 @@ * Internal dependencies */ import { + ACTION_MODAL_EDITOR_CONTENT_HAS_CHANGED, ACTION_MODAL_EDITOR_CLOSE, ACTION_MODAL_EDITOR_OPEN, + ACTION_MODAL_EDITOR_SET_BLOCKS, } from './constants'; import type { ProductEditorModalEditorAction, @@ -16,6 +18,8 @@ import type { const INITIAL_STATE: ProductEditorUIStateProps = { modalEditor: { isOpen: false, + blocks: [], + hasChanged: false, }, }; @@ -28,16 +32,37 @@ export default function reducer( return { ...state, modalEditor: { + ...state.modalEditor, isOpen: true, }, }; + case ACTION_MODAL_EDITOR_CLOSE: return { ...state, modalEditor: { + ...state.modalEditor, isOpen: false, }, }; + + case ACTION_MODAL_EDITOR_SET_BLOCKS: + return { + ...state, + modalEditor: { + ...state.modalEditor, + blocks: action.blocks || [], + }, + }; + + case ACTION_MODAL_EDITOR_CONTENT_HAS_CHANGED: + return { + ...state, + modalEditor: { + ...state.modalEditor, + hasChanged: action?.hasChanged || false, + }, + }; } return state; diff --git a/packages/js/product-editor/src/store/product-editor-ui/selectors.ts b/packages/js/product-editor/src/store/product-editor-ui/selectors.ts index bced6734a3b..ab5191793d3 100644 --- a/packages/js/product-editor/src/store/product-editor-ui/selectors.ts +++ b/packages/js/product-editor/src/store/product-editor-ui/selectors.ts @@ -1,7 +1,11 @@ +/** + * External dependencies + */ +import { BlockInstance } from '@wordpress/blocks'; + /** * Internal dependencies */ - import type { ProductEditorUIStateProps } from './types'; export default { @@ -10,4 +14,16 @@ export default { ) { return state.modalEditor.isOpen; }, + + getModalEditorBlocks: function getModalEditorBlocks( + state: ProductEditorUIStateProps + ): BlockInstance[] { + return state.modalEditor.blocks; + }, + + getModalEditorContentHasChanged: function getModalEditorContentHasChanged( + state: ProductEditorUIStateProps + ): boolean { + return !! state.modalEditor.hasChanged; + }, }; diff --git a/packages/js/product-editor/src/store/product-editor-ui/types.ts b/packages/js/product-editor/src/store/product-editor-ui/types.ts index 1ef8c1a8e04..5fc022bea18 100644 --- a/packages/js/product-editor/src/store/product-editor-ui/types.ts +++ b/packages/js/product-editor/src/store/product-editor-ui/types.ts @@ -1,17 +1,34 @@ +/** + * External dependencies + */ +import { BlockInstance } from '@wordpress/blocks'; + /** * Internal dependencies */ import { + ACTION_MODAL_EDITOR_CONTENT_HAS_CHANGED, ACTION_MODAL_EDITOR_CLOSE, ACTION_MODAL_EDITOR_OPEN, + ACTION_MODAL_EDITOR_SET_BLOCKS, } from './constants'; export type ProductEditorUIStateProps = { modalEditor: { isOpen: boolean; + blocks: BlockInstance[]; + hasChanged?: boolean; }; }; export type ProductEditorModalEditorAction = { - type: typeof ACTION_MODAL_EDITOR_OPEN | typeof ACTION_MODAL_EDITOR_CLOSE; + type: + | typeof ACTION_MODAL_EDITOR_OPEN + | typeof ACTION_MODAL_EDITOR_CLOSE + | typeof ACTION_MODAL_EDITOR_SET_BLOCKS + | typeof ACTION_MODAL_EDITOR_CONTENT_HAS_CHANGED; + + blocks?: BlockInstance[]; + + hasChanged?: boolean; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3b98d7feb09..5f5fa826010 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26283,7 +26283,6 @@ packages: /flow-parser@0.223.3: resolution: {integrity: sha512-9KxxDKSB22ovMpSULbOL/QAQGPN6M0YMS3PubQvB0jVc4W7QP6VhasIVic7MzKcJSh0BAVs4J6SZjoH0lDDNlg==} engines: {node: '>=0.4.0'} - dev: true /flush-write-stream@1.1.1: resolution: {integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==} @@ -31875,7 +31874,7 @@ packages: '@babel/register': 7.22.15(@babel/core@7.23.5) babel-core: 7.0.0-bridge.0(@babel/core@7.23.5) chalk: 4.1.2 - flow-parser: 0.206.0 + flow-parser: 0.223.3 graceful-fs: 4.2.11 micromatch: 4.0.5 neo-async: 2.6.2