[Product Block Editor]: move Modal editor out of the description block edit component (#41878)

This commit is contained in:
Damián Suárez 2023-12-06 18:28:36 -03:00 committed by GitHub
commit 6fab5dc8eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 168 additions and 41 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: update
[Product Block Editor]: move Modal edittor out of the description block edit component

View File

@ -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 (
<div { ...blockProps }>
<Button
variant="secondary"
onClick={ () => {
if ( description ) {
setModalEditorBlocks( parse( description ) );
}
openModalEditor();
recordEvent( 'product_add_description_click' );
} }
@ -80,20 +103,6 @@ export function DescriptionBlockEdit( {
: __( 'Add description', 'woocommerce' ) }
</Button>
{ isModalEditorOpen && (
<ModalEditor
initialBlocks={ parse( description ) }
onChange={ ( blocks ) => {
const html = serialize(
clearDescriptionIfEmpty( blocks )
);
setDescription( html );
} }
onClose={ closeModalEditor }
title={ __( 'Edit description', 'woocommerce' ) }
/>
) }
{ !! description.length && (
<ContentPreview content={ description } />
) }

View File

@ -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 (
<ModalEditor
onClose={ closeModalEditor }
title={ __( 'Edit description', 'woocommerce' ) }
/>
);
}
return (
<div className="woocommerce-product-block-editor">
<BlockContextProvider value={ context }>
@ -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. */ }

View File

@ -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?.();
} }
/>
<div className="woocommerce-iframe-editor__main">
@ -157,7 +168,7 @@ export function IframeEditor( {
{ /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ }
{ /* @ts-ignore */ }
<BlockEditorKeyboardShortcuts.Register />
{ onClose && (
{ showBackButton && onClose && (
<BackButton
onClick={ () => {
setTimeout( onClose, 550 );

View File

@ -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 }
/>
</Modal>
);

View File

@ -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 {

View File

@ -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';

View File

@ -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;

View File

@ -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;
},
};

View File

@ -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;
};

View File

@ -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