diff --git a/docs/reference-guides/data/data-core-customize-widgets.md b/docs/reference-guides/data/data-core-customize-widgets.md index 8796f4cfea5ddf..b0c0fc8818f327 100644 --- a/docs/reference-guides/data/data-core-customize-widgets.md +++ b/docs/reference-guides/data/data-core-customize-widgets.md @@ -78,6 +78,7 @@ _Parameters_ - _value_ `boolean|Object`: Whether the inserter should be opened (true) or closed (false). To specify an insertion point, use an object. - _value.rootClientId_ `string`: The root client ID to insert at. - _value.insertionIndex_ `number`: The index to insert at. +- _value.initialTab_ `string`: The id of the tab to display first when the block editor inserter is opened. A category corresponds to one of the tab ids defined in packages/block-editor/src/components/inserter/tabs.js. _Returns_ diff --git a/docs/reference-guides/data/data-core-edit-widgets.md b/docs/reference-guides/data/data-core-edit-widgets.md index 2dde89bf69ad73..f5268963882bab 100644 --- a/docs/reference-guides/data/data-core-edit-widgets.md +++ b/docs/reference-guides/data/data-core-edit-widgets.md @@ -215,6 +215,7 @@ _Parameters_ - _value_ `boolean|Object`: Whether the inserter should be opened (true) or closed (false). To specify an insertion point, use an object. - _value.rootClientId_ `string`: The root client ID to insert at. - _value.insertionIndex_ `number`: The index to insert at. +- _value.initialTab_ `string`: The id of the tab to display first when the block editor inserter is opened. A category corresponds to one of the tab ids defined in packages/block-editor/src/components/inserter/tabs.js. _Returns_ diff --git a/docs/reference-guides/data/data-core-editor.md b/docs/reference-guides/data/data-core-editor.md index be2276bbf03641..51126696942fad 100644 --- a/docs/reference-guides/data/data-core-editor.md +++ b/docs/reference-guides/data/data-core-editor.md @@ -1382,6 +1382,7 @@ _Parameters_ - _value_ `boolean|Object`: Whether the inserter should be opened (true) or closed (false). To specify an insertion point, use an object. - _value.rootClientId_ `string`: The root client ID to insert at. - _value.insertionIndex_ `number`: The index to insert at. +- _value.initialTab_ `string`: The id of the tab to display first when the block editor inserter is opened. A category corresponds to one of the tab ids defined in packages/block-editor/src/components/inserter/tabs.js. _Returns_ diff --git a/packages/block-editor/src/components/inserter/library.js b/packages/block-editor/src/components/inserter/library.js index 7d462b343c310d..870bb7b91b29c3 100644 --- a/packages/block-editor/src/components/inserter/library.js +++ b/packages/block-editor/src/components/inserter/library.js @@ -21,6 +21,7 @@ function InserterLibrary( showMostUsedBlocks = false, __experimentalInsertionIndex, __experimentalFilterValue, + initialInserterTab, onSelect = noop, shouldFocusBlock = false, }, @@ -46,6 +47,7 @@ function InserterLibrary( isAppender={ isAppender } showInserterHelpPanel={ showInserterHelpPanel } showMostUsedBlocks={ showMostUsedBlocks } + initialInserterTab={ initialInserterTab } __experimentalInsertionIndex={ __experimentalInsertionIndex } __experimentalFilterValue={ __experimentalFilterValue } shouldFocusBlock={ shouldFocusBlock } diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index 6a38e52cbffba1..4527318813a3da 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -45,6 +45,7 @@ function InserterMenu( showMostUsedBlocks, __experimentalFilterValue = '', shouldFocusBlock = true, + initialInserterTab, }, ref ) { @@ -56,8 +57,9 @@ function InserterMenu( const [ patternFilter, setPatternFilter ] = useState( 'all' ); const [ selectedMediaCategory, setSelectedMediaCategory ] = useState( null ); - const [ selectedTab, setSelectedTab ] = useState( null ); - + const [ selectedTab, setSelectedTab ] = useState( + initialInserterTab || null + ); const [ destinationRootClientId, onInsertBlocks, onToggleInsertionPoint ] = useInsertionPoint( { rootClientId, @@ -259,6 +261,7 @@ function InserterMenu( showMedia={ showMedia } onSelect={ handleSetSelectedTab } tabsContents={ inserterTabsContents } + initialTabId={ initialInserterTab } /> ) } { ! delayedFilterValue && ! showAsTabs && ( diff --git a/packages/block-editor/src/components/inserter/tabs.js b/packages/block-editor/src/components/inserter/tabs.js index 4795c3ce4fdc24..1ea917596711ac 100644 --- a/packages/block-editor/src/components/inserter/tabs.js +++ b/packages/block-editor/src/components/inserter/tabs.js @@ -33,6 +33,7 @@ function InserterTabs( { showMedia = false, onSelect, tabsContents, + initialTabId, } ) { const tabs = [ blocksTab, @@ -40,9 +41,13 @@ function InserterTabs( { showMedia && mediaTab, ].filter( Boolean ); + initialTabId = !! tabs.find( ( { name } ) => initialTabId === name ) + ? initialTabId + : 'blocks'; + return (
- + { tabs.map( ( tab ) => ( diff --git a/packages/block-library/src/editor.scss b/packages/block-library/src/editor.scss index e3642868034a5a..e2a9262362706a 100644 --- a/packages/block-library/src/editor.scss +++ b/packages/block-library/src/editor.scss @@ -32,6 +32,7 @@ @import "./nextpage/editor.scss"; @import "./page-list/editor.scss"; @import "./paragraph/editor.scss"; +@import "./post-content/editor.scss"; @import "./post-excerpt/editor.scss"; @import "./pullquote/editor.scss"; @import "./rss/editor.scss"; diff --git a/packages/block-library/src/post-content/edit.js b/packages/block-library/src/post-content/edit.js index ae3bb938c90eee..ec491210b38270 100644 --- a/packages/block-library/src/post-content/edit.js +++ b/packages/block-library/src/post-content/edit.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ @@ -8,13 +13,14 @@ import { __experimentalRecursionProvider as RecursionProvider, __experimentalUseHasRecursion as useHasRecursion, Warning, + store as blockEditorStore, } from '@wordpress/block-editor'; -import { - useEntityProp, - useEntityBlockEditor, - store as coreStore, -} from '@wordpress/core-data'; -import { useSelect } from '@wordpress/data'; +import { useEntityProp, useEntityBlockEditor } from '@wordpress/core-data'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { Placeholder, Button } from '@wordpress/components'; +import { postContent as icon } from '@wordpress/icons'; +import { createBlock } from '@wordpress/blocks'; + /** * Internal dependencies */ @@ -45,40 +51,104 @@ function ReadOnlyContent( { ); } -function EditableContent( { context = {} } ) { - const { postType, postId } = context; +function EmptyContentPlaceholder( { context, onClose, openInserter } ) { + const { postType } = context; + const label = + 'page' === postType + ? __( 'This page’s content is empty' ) + : __( 'This post’s content is empty' ); + return ( + + + + + + ); +} + +function PostContentPlaceholder( { layoutClassNames } ) { + const blockProps = useBlockProps( { className: layoutClassNames } ); + return ( +
+ +

+ { __( 'This block will be replaced with your content.' ) } +

+
+
+ ); +} +function EditableContent( { context = {}, clientId } ) { + const { postType, postId } = context; + const { selectBlock, insertBlock } = useDispatch( blockEditorStore ); const [ blocks, onInput, onChange ] = useEntityBlockEditor( 'postType', postType, { id: postId } ); - const entityRecord = useSelect( - ( select ) => { - return select( coreStore ).getEntityRecord( - 'postType', - postType, - postId - ); - }, + const setInserterIsOpened = useSelect( + ( select ) => + select( blockEditorStore ).getSettings() + .__experimentalSetIsInserterOpened, [ postType, postId ] ); - const hasInnerBlocks = !! entityRecord?.content?.raw || blocks?.length; - - const initialInnerBlocks = [ [ 'core/paragraph' ] ]; + const hasInnerBlocks = blocks?.length; - const props = useInnerBlocksProps( - useBlockProps( { className: 'entry-content' } ), + const { children, ...props } = useInnerBlocksProps( + useBlockProps( { + className: classnames( 'entry-content', { + 'wp-block-post-content__placeholder': ! hasInnerBlocks, + } ), + } ), { value: blocks, onInput, onChange, - template: ! hasInnerBlocks ? initialInnerBlocks : undefined, } ); - return
; + + const onClose = () => { + const initialBlock = createBlock( 'core/paragraph' ); + insertBlock( initialBlock, 0, clientId ); + selectBlock( initialBlock.clientId ); + }; + + const openInserter = () => { + setInserterIsOpened( { + initialTab: 'patterns', + rootClientId: clientId, + insertionIndex: 0, + } ); + }; + + return ( +
+ { children } + { ! hasInnerBlocks && ( + + ) } +
+ ); } function Content( props ) { @@ -104,29 +174,6 @@ function Content( props ) { ); } -function Placeholder( { layoutClassNames } ) { - const blockProps = useBlockProps( { className: layoutClassNames } ); - return ( -
-

- { __( - 'This is the Content block, it will display all the blocks in any single post or page.' - ) } -

-

- { __( - 'That might be a simple arrangement like consecutive paragraphs in a blog post, or a more elaborate composition that includes image galleries, videos, tables, columns, and any other block types.' - ) } -

-

- { __( - 'If there are any Custom Post Types registered at your site, the Content block can display the contents of those entries as well.' - ) } -

-
- ); -} - function RecursionError() { const blockProps = useBlockProps(); return ( @@ -139,6 +186,7 @@ function RecursionError() { } export default function PostContentEdit( { + clientId, context, __unstableLayoutClassNames: layoutClassNames, } ) { @@ -153,11 +201,12 @@ export default function PostContentEdit( { { contextPostId && contextPostType ? ( ) : ( - + ) } ); diff --git a/packages/block-library/src/post-content/editor.scss b/packages/block-library/src/post-content/editor.scss new file mode 100644 index 00000000000000..4c4ecba63d90f2 --- /dev/null +++ b/packages/block-library/src/post-content/editor.scss @@ -0,0 +1,26 @@ +.wp-block-post-content__placeholder { + .components-placeholder { + min-height: auto; + box-shadow: none; + padding: $grid-unit-30; + } +} + +.wp-block-post-content__content-placeholder { + &.has-illustration::before { + border: 1px solid currentColor; + background: none; + } + .components-placeholder__illustration { + opacity: 0.1; + } + .components-placeholder__fieldset { + font-size: inherit; + font-family: inherit; + align-self: center; + p { + font-size: inherit; + font-family: inherit; + } + } +} diff --git a/packages/customize-widgets/src/store/actions.js b/packages/customize-widgets/src/store/actions.js index 8656ef8854c9f5..528f2c395d57e6 100644 --- a/packages/customize-widgets/src/store/actions.js +++ b/packages/customize-widgets/src/store/actions.js @@ -7,6 +7,8 @@ * use an object. * @param {string} value.rootClientId The root client ID to insert at. * @param {number} value.insertionIndex The index to insert at. + * @param {string} value.initialTab The id of the tab to display first when the block editor inserter is opened. + * A category corresponds to one of the tab ids defined in packages/block-editor/src/components/inserter/tabs.js. * * @example * ```js diff --git a/packages/edit-widgets/src/store/actions.js b/packages/edit-widgets/src/store/actions.js index 8124ace66bdb3a..40d48bd85fab25 100644 --- a/packages/edit-widgets/src/store/actions.js +++ b/packages/edit-widgets/src/store/actions.js @@ -348,6 +348,8 @@ export function setIsWidgetAreaOpen( clientId, isOpen ) { * use an object. * @param {string} value.rootClientId The root client ID to insert at. * @param {number} value.insertionIndex The index to insert at. + * @param {string} value.initialTab The id of the tab to display first when the block editor inserter is opened. + * A category corresponds to one of the tab ids defined in packages/block-editor/src/components/inserter/tabs.js. * * @return {Object} Action object. */ diff --git a/packages/editor/src/components/inserter-sidebar/index.js b/packages/editor/src/components/inserter-sidebar/index.js index 7db4335309935a..783294dddee7d1 100644 --- a/packages/editor/src/components/inserter-sidebar/index.js +++ b/packages/editor/src/components/inserter-sidebar/index.js @@ -20,14 +20,18 @@ import { unlock } from '../../lock-unlock'; import { store as editorStore } from '../../store'; export default function InserterSidebar() { - const { insertionPoint, showMostUsedBlocks } = useSelect( ( select ) => { - const { getInsertionPoint } = unlock( select( editorStore ) ); - const { get } = select( preferencesStore ); - return { - insertionPoint: getInsertionPoint(), - showMostUsedBlocks: get( 'core', 'mostUsedBlocks' ), - }; - }, [] ); + const { insertionPoint, initialInserterTab, showMostUsedBlocks } = + useSelect( ( select ) => { + const { getInsertionPoint, getInserterInitialTab } = unlock( + select( editorStore ) + ); + const { get } = select( preferencesStore ); + return { + insertionPoint: getInsertionPoint(), + initialInserterTab: getInserterInitialTab(), + showMostUsedBlocks: get( 'core', 'mostUsedBlocks' ), + }; + }, [] ); const { setIsInserterOpened } = useDispatch( editorStore ); const isMobileViewport = useViewportMatch( 'medium', '<' ); @@ -64,6 +68,7 @@ export default function InserterSidebar() { __experimentalInsertionIndex={ insertionPoint.insertionIndex } + initialInserterTab={ initialInserterTab } __experimentalFilterValue={ insertionPoint.filterValue } ref={ libraryRef } /> diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index a0330321bac8f7..9f0bf4700c4715 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -697,6 +697,8 @@ export function removeEditorPanel( panelName ) { * use an object. * @param {string} value.rootClientId The root client ID to insert at. * @param {number} value.insertionIndex The index to insert at. + * @param {string} value.initialTab The id of the tab to display first when the block editor inserter is opened. + * A category corresponds to one of the tab ids defined in packages/block-editor/src/components/inserter/tabs.js. * * @return {Object} Action object. */ diff --git a/packages/editor/src/store/private-selectors.js b/packages/editor/src/store/private-selectors.js index e276859f884038..f3dd3f4404e24e 100644 --- a/packages/editor/src/store/private-selectors.js +++ b/packages/editor/src/store/private-selectors.js @@ -25,7 +25,11 @@ const EMPTY_INSERTION_POINT = { export const getInsertionPoint = createRegistrySelector( ( select ) => ( state ) => { if ( typeof state.blockInserterPanel === 'object' ) { - return state.blockInserterPanel; + return { + rootClientId: state.blockInserterPanel?.rootClientId, + insertionIndex: state.blockInserterPanel?.insertionIndex, + filterValue: state.blockInserterPanel?.filterValue, + }; } if ( getRenderingMode( state ) === 'template-locked' ) { @@ -46,6 +50,21 @@ export const getInsertionPoint = createRegistrySelector( } ); +/** + * Get the initial tab id for the inserter. + * A category corresponds to one of the tab ids defined in packages/block-editor/src/components/inserter/tabs.js. + * + * @param {Object} state Global application state. + * + * @return {string} The initial tab category to open when the inserter is opened. + */ +export const getInserterInitialTab = createRegistrySelector( + () => ( state ) => + typeof state.blockInserterPanel === 'object' + ? state.blockInserterPanel?.initialTab + : null +); + export function getListViewToggleRef( state ) { return state.listViewToggleRef; } diff --git a/test/e2e/specs/site-editor/pages.spec.js b/test/e2e/specs/site-editor/pages.spec.js index a04359730421b5..e3b12ca7126174 100644 --- a/test/e2e/specs/site-editor/pages.spec.js +++ b/test/e2e/specs/site-editor/pages.spec.js @@ -23,8 +23,8 @@ async function addPageContent( editor, page ) { .getByRole( 'document', { name: 'Block: Content', } ) - .getByRole( 'document', { - name: 'Empty block; start writing or type forward slash to choose a block', + .getByRole( 'button', { + name: 'Start blank', } ) .click(); @@ -124,9 +124,7 @@ test.describe( 'Pages', () => { editor.canvas.getByRole( 'document', { name: 'Block: Content', } ) - ).toContainText( - 'This is the Content block, it will display all the blocks in any single post or page.' - ); + ).toContainText( 'This block will be replaced with your content.' ); await expect( page.locator( 'role=button[name="Dismiss this notice"i] >> text="Editing template. Changes made here affect all posts and pages that use the template."'