Skip to content

Commit

Permalink
Site Editor: Add 'Show template' toggle when editing pages (#52674)
Browse files Browse the repository at this point in the history
* Site Editor: First pass at adding a Show Template toggle when editing pages

* Bit of clean up

* Use a toggle in sidebar

* Make toggling 'Show template' on/off faster

* Use blocks in same order as template

* Tidy up code

* Add unit tests

* Add doc comment

* Formatting
Remove unused component import
Doc update

* With icon and updated copy

* Use only the check icon and set min width of dropdown to minimize the effect of the dropdown width toggling between narrow and wide

* This commit tries to get around a peculiarity of __experimentalGetGlobalBlocksByName, which doesn't seem to be able to find the post-content block in a new block tree (if it even exists in state).
We're sidestepping the issue, but with fewer selectors, so overall hopefully it's more sustainable.

* Removing the filter iteration layer and adding it to the flattenBlock helper function
Checking for disallowed blocks as well, e.g., core/query

* Extract usePageContentBlocks and write basic tests
Moving PAGE_CONTENT_BLOCK_TYPES to utils/constants and creating a map

* Updates selectors to include canvas mode so we can show any hidden templates in focus mode

* Moving getPageContentFocusType and setPageContentFocusType selector/action to locked, private methods
In the use-page-content-blocks.js test, add innerblocks to the query test block

---------

Co-authored-by: ramon <ramonjd@gmail.com>
  • Loading branch information
noisysocks and ramonjd authored Sep 20, 2023
1 parent 1873738 commit 8f3f218
Show file tree
Hide file tree
Showing 16 changed files with 406 additions and 115 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* WordPress dependencies
*/
import { useEntityBlockEditor } from '@wordpress/core-data';
import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../../store';
import { unlock } from '../../../lock-unlock';
import useSiteEditorSettings from '../use-site-editor-settings';
import usePageContentBlocks from './use-page-content-blocks';

const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis );

const noop = () => {};

/**
* The default block editor provider for the site editor. Typically used when
* the post type is `'wp_template_part'` or `'wp_template'` and allows editing
* of the template and its nested entities.
*
* If the page content focus type is `'hideTemplate'`, the provider will provide
* a set of page content blocks wrapped in a container that, together,
* mimic the look and feel of the post editor and
* allow editing of the page content only.
*
* @param {Object} props
* @param {WPElement} props.children
*/
export default function DefaultBlockEditorProvider( { children } ) {
const settings = useSiteEditorSettings();

const { templateType, isTemplateHidden } = useSelect( ( select ) => {
const { getEditedPostType } = select( editSiteStore );
const { getPageContentFocusType, getCanvasMode } = unlock(
select( editSiteStore )
);
return {
templateType: getEditedPostType(),
isTemplateHidden:
getCanvasMode() === 'edit' &&
getPageContentFocusType() === 'hideTemplate',
canvasMode: unlock( select( editSiteStore ) ).getCanvasMode(),
};
}, [] );

const [ blocks, onInput, onChange ] = useEntityBlockEditor(
'postType',
templateType
);
const pageContentBlock = usePageContentBlocks( blocks, isTemplateHidden );
return (
<ExperimentalBlockEditorProvider
settings={ settings }
value={
isTemplateHidden && pageContentBlock.length
? pageContentBlock
: blocks
}
onInput={ isTemplateHidden ? noop : onInput }
onChange={ isTemplateHidden ? noop : onChange }
useSubRegistry={ false }
>
{ children }
</ExperimentalBlockEditorProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../../store';
import DefaultBlockEditorProvider from './default-block-editor-provider';
import NavigationBlockEditorProvider from './navigation-block-editor-provider';

export default function BlockEditorProvider( { children } ) {
const entityType = useSelect(
( select ) => select( editSiteStore ).getEditedPostType(),
[]
);
if ( entityType === 'wp_navigation' ) {
return (
<NavigationBlockEditorProvider>
{ children }
</NavigationBlockEditorProvider>
);
}
return (
<DefaultBlockEditorProvider>{ children }</DefaultBlockEditorProvider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* External dependencies
*/
import { renderHook } from '@testing-library/react';
/**
* WordPress dependencies
*/
import { createBlock } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import usePageContentBlocks from '../use-page-content-blocks';

jest.mock( '@wordpress/blocks', () => {
return {
__esModule: true,
...jest.requireActual( '@wordpress/blocks' ),
createBlock( name, attributes = {}, innerBlocks = [] ) {
return {
name,
attributes,
innerBlocks,
};
},
};
} );

describe( 'usePageContentBlocks', () => {
const blocksList = [
createBlock( 'core/group', {}, [
createBlock( 'core/post-title' ),
createBlock( 'core/post-featured-image' ),
createBlock( 'core/query', {}, [
createBlock( 'core/post-title' ),
createBlock( 'core/post-featured-image' ),
createBlock( 'core/post-content' ),
] ),
createBlock( 'core/post-content' ),
] ),
createBlock( 'core/query' ),
createBlock( 'core/paragraph' ),
createBlock( 'core/post-content' ),
];
it( 'should return empty array if `isPageContentFocused` is `false`', () => {
const { result } = renderHook( () =>
usePageContentBlocks( blocksList, false )
);
expect( result.current ).toEqual( [] );
} );
it( 'should return empty array if `blocks` is undefined', () => {
const { result } = renderHook( () =>
usePageContentBlocks( undefined, true )
);
expect( result.current ).toEqual( [] );
} );
it( 'should return empty array if `blocks` is an empty array', () => {
const { result } = renderHook( () => usePageContentBlocks( [], true ) );
expect( result.current ).toEqual( [] );
} );
it( 'should return new block list', () => {
const { result } = renderHook( () =>
usePageContentBlocks( blocksList, true )
);
expect( result.current ).toEqual( [
{
name: 'core/group',
attributes: {
layout: { type: 'constrained' },
style: {
spacing: {
margin: {
top: '4em', // Mimics the post editor.
},
},
},
},
innerBlocks: [
createBlock( 'core/post-title' ),
createBlock( 'core/post-featured-image' ),
createBlock( 'core/post-content' ),
createBlock( 'core/post-content' ),
],
},
] );
} );
} );
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* WordPress dependencies
*/
import { useMemo } from '@wordpress/element';
import { createBlock } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { PAGE_CONTENT_BLOCK_TYPES } from '../../../utils/constants';

/**
* Helper method to iterate through all blocks, recursing into allowed inner blocks.
* Returns a flattened object of transformed blocks.
*
* @param {Array} blocks Blocks to flatten.
* @param {Function} transform Transforming function to be applied to each block. If transform returns `undefined`, the block is skipped.
*
* @return {Array} Flattened object.
*/
function flattenBlocks( blocks, transform ) {
const result = [];
for ( let i = 0; i < blocks.length; i++ ) {
// Since the Query Block could contain PAGE_CONTENT_BLOCK_TYPES block types,
// we skip it because we only want to render stand-alone page content blocks in the block list.
if ( [ 'core/query' ].includes( blocks[ i ].name ) ) {
continue;
}
const transformedBlock = transform( blocks[ i ] );
if ( transformedBlock ) {
result.push( transformedBlock );
}
result.push( ...flattenBlocks( blocks[ i ].innerBlocks, transform ) );
}

return result;
}

/**
* Returns a memoized array of blocks that contain only page content blocks,
* surrounded by a group block to mimic the post editor.
*
* @param {Array} blocks Block list.
* @param {boolean} isPageContentFocused Whether the page content has focus. If `true` return page content blocks. Default `false`.
*
* @return {Array} Page content blocks.
*/
export default function usePageContentBlocks(
blocks,
isPageContentFocused = false
) {
return useMemo( () => {
if ( ! isPageContentFocused || ! blocks || ! blocks.length ) {
return [];
}
return [
createBlock(
'core/group',
{
layout: { type: 'constrained' },
style: {
spacing: {
margin: {
top: '4em', // Mimics the post editor.
},
},
},
},
flattenBlocks( blocks, ( block ) => {
if ( PAGE_CONTENT_BLOCK_TYPES[ block.name ] ) {
return createBlock( block.name );
}
} )
),
];
}, [ blocks, isPageContentFocused ] );
}

This file was deleted.

15 changes: 1 addition & 14 deletions packages/edit-site/src/components/block-editor/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { BlockInspector } from '@wordpress/block-editor';
import { privateApis as editPatternsPrivateApis } from '@wordpress/patterns';

Expand All @@ -10,31 +9,19 @@ import { privateApis as editPatternsPrivateApis } from '@wordpress/patterns';
*/
import TemplatePartConverter from '../template-part-converter';
import { SidebarInspectorFill } from '../sidebar-edit-mode';
import { store as editSiteStore } from '../../store';
import SiteEditorCanvas from './site-editor-canvas';
import getBlockEditorProvider from './get-block-editor-provider';
import BlockEditorProvider from './block-editor-provider';

import { unlock } from '../../lock-unlock';
const { PatternsMenuItems } = unlock( editPatternsPrivateApis );
export default function BlockEditor() {
const entityType = useSelect(
( select ) => select( editSiteStore ).getEditedPostType(),
[]
);

// Choose the provider based on the entity type currently
// being edited.
const BlockEditorProvider = getBlockEditorProvider( entityType );

return (
<BlockEditorProvider>
<TemplatePartConverter />
<SidebarInspectorFill>
<BlockInspector />
</SidebarInspectorFill>

<SiteEditorCanvas />

<PatternsMenuItems />
</BlockEditorProvider>
);
Expand Down

This file was deleted.

Loading

1 comment on commit 8f3f218

@github-actions
Copy link

@github-actions github-actions bot commented on 8f3f218 Sep 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in 8f3f218.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/6254948394
📝 Reported issues:

Please sign in to comment.