Skip to content

Commit

Permalink
Replace Starter Content modal with inserter panel (#66836)
Browse files Browse the repository at this point in the history
* Inserter: Add 'Starter Content' category to the inserter

* Add cat condition

* Replace Starter Content modal with inserter panel

* remove option condition

* remove duplicated category

* Attempt to fix media panel test

* Attempt to fix style variations test

* Attempt to fix recursive pattern test

* Attempt to fix template resolution test

* Attempt to fix new page test

* Tweak template resolution test

* Remove categoryObject

* Fix aria-current on category tabs

* Fix selected cat in patterns explorer

* pass postId so that the inserter gets reopened when a new page is created using the command palette

* Take control of zoom if a new page is created

* Add post Id back

---------

Co-authored-by: Andrei Draganescu <[email protected]>
Co-authored-by: Sarah Norris <[email protected]>
Co-authored-by: Ben Dwyer <[email protected]>
  • Loading branch information
4 people authored Dec 20, 2024
1 parent a9f93d8 commit 1185005
Show file tree
Hide file tree
Showing 12 changed files with 68 additions and 151 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ import { usePatternCategories } from '../block-patterns-tab/use-pattern-categori

function PatternsExplorer( { initialCategory, rootClientId } ) {
const [ searchValue, setSearchValue ] = useState( '' );
const [ selectedCategory, setSelectedCategory ] = useState(
initialCategory?.name
);
const [ selectedCategory, setSelectedCategory ] =
useState( initialCategory );

const patternCategories = usePatternCategories( rootClientId );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ function BlockPatternsTab( {
) }
{ showPatternsExplorer && (
<PatternsExplorerModal
initialCategory={ selectedCategory || categories[ 0 ] }
initialCategory={
selectedCategory?.name || categories[ 0 ]?.name
}
patternCategories={ categories }
onModalClose={ () => setShowPatternsExplorer( false ) }
rootClientId={ rootClientId }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,19 @@ export function PatternCategoryPreviews( {
return false;
}

if ( category.name === allPatternsCategory.name ) {
if ( category.name === allPatternsCategory?.name ) {
return true;
}

if (
category.name === myPatternsCategory.name &&
category.name === myPatternsCategory?.name &&
pattern.type === INSERTER_PATTERN_TYPES.user
) {
return true;
}

if (
category.name === starterPatternsCategory.name &&
category.name === starterPatternsCategory?.name &&
pattern.blockTypes?.includes( 'core/post-content' )
) {
return true;
Expand Down Expand Up @@ -149,7 +149,7 @@ export function PatternCategoryPreviews( {
level={ 4 }
as="div"
>
{ category.label }
{ category?.label }
</Heading>
</FlexBlock>
<PatternsFilter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
const getShouldDisableSyncFilter = ( sourceFilter ) =>
sourceFilter !== 'all' && sourceFilter !== 'user';
const getShouldHideSourcesFilter = ( category ) => {
return category.name === myPatternsCategory.name;
return category?.name === myPatternsCategory.name;
};

const PATTERN_SOURCE_MENU_OPTIONS = [
Expand Down Expand Up @@ -60,7 +60,7 @@ export function PatternsFilter( {
// the user may be confused when switching to another category if the haven't explicity set
// this filter themselves.
const currentPatternSourceFilter =
category.name === myPatternsCategory.name
category?.name === myPatternsCategory.name
? INSERTER_PATTERN_TYPES.user
: patternSourceFilter;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ function CategoryTabs( {
key={ category.name }
tabId={ category.name }
aria-current={
category === selectedCategory ? 'true' : undefined
category.name === selectedCategory?.name
? 'true'
: undefined
}
>
{ category.label }
Expand Down
12 changes: 10 additions & 2 deletions packages/block-editor/src/hooks/use-zoom-out.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { useEffect, useRef } from '@wordpress/element';
import { useEffect, useRef, useContext } from '@wordpress/element';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../store';
import { unlock } from '../lock-unlock';
import BlockContext from '../components/block-context';

/**
* A hook used to set the editor mode to zoomed out mode, invoking the hook sets the mode.
Expand All @@ -19,6 +20,7 @@ import { unlock } from '../lock-unlock';
* @param {boolean} enabled If we should enter into zoomOut mode or not
*/
export function useZoomOut( enabled = true ) {
const { postId } = useContext( BlockContext );
const { setZoomLevel, resetZoomLevel } = unlock(
useDispatch( blockEditorStore )
);
Expand All @@ -37,6 +39,7 @@ export function useZoomOut( enabled = true ) {

const controlZoomLevelRef = useRef( false );
const isEnabledRef = useRef( enabled );
const postIdRef = useRef( postId );

/**
* This hook tracks if the zoom state was changed manually by the user via clicking
Expand All @@ -55,6 +58,11 @@ export function useZoomOut( enabled = true ) {
useEffect( () => {
isEnabledRef.current = enabled;

// If the user created a new post/page, we should take control of the zoom level.
if ( postIdRef.current !== postId ) {
controlZoomLevelRef.current = true;
}

if ( enabled !== isZoomOut() ) {
controlZoomLevelRef.current = true;

Expand All @@ -71,5 +79,5 @@ export function useZoomOut( enabled = true ) {
resetZoomLevel();
}
};
}, [ enabled, isZoomOut, resetZoomLevel, setZoomLevel ] );
}, [ enabled, isZoomOut, postId, resetZoomLevel, setZoomLevel ] );
}
21 changes: 8 additions & 13 deletions packages/editor/src/components/preferences-modal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import PageAttributesCheck from '../page-attributes/check';
import PostTypeSupportCheck from '../post-type-support-check';
import { store as editorStore } from '../../store';
import { unlock } from '../../lock-unlock';
import { useStartPatterns } from '../start-page-options';

const {
PreferencesModal,
Expand Down Expand Up @@ -73,7 +72,6 @@ function PreferencesModalContents( { extraSections = {} } ) {
const { setIsListViewOpened, setIsInserterOpened } =
useDispatch( editorStore );
const { set: setPreference } = useDispatch( preferencesStore );
const hasStarterPatterns = !! useStartPatterns().length;

const sections = useMemo(
() =>
Expand Down Expand Up @@ -114,16 +112,14 @@ function PreferencesModalContents( { extraSections = {} } ) {
'Allow right-click contextual menus'
) }
/>
{ hasStarterPatterns && (
<PreferenceToggleControl
scope="core"
featureName="enableChoosePatternModal"
help={ __(
'Shows starter patterns when creating a new page.'
) }
label={ __( 'Show starter patterns' ) }
/>
) }
<PreferenceToggleControl
scope="core"
featureName="enableChoosePatternModal"
help={ __(
'Shows starter patterns when creating a new page.'
) }
label={ __( 'Show starter patterns' ) }
/>
</PreferencesModalSection>
<PreferencesModalSection
title={ __( 'Document settings' ) }
Expand Down Expand Up @@ -341,7 +337,6 @@ function PreferencesModalContents( { extraSections = {} } ) {
setIsListViewOpened,
setPreference,
isLargeViewport,
hasStarterPatterns,
]
);

Expand Down
147 changes: 28 additions & 119 deletions packages/editor/src/components/start-page-options/index.js
Original file line number Diff line number Diff line change
@@ -1,141 +1,50 @@
/**
* WordPress dependencies
*/
import { Modal } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useState, useMemo } from '@wordpress/element';
import {
store as blockEditorStore,
__experimentalBlockPatternsList as BlockPatternsList,
} from '@wordpress/block-editor';
import { useEffect } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { __unstableSerializeAndClean } from '@wordpress/blocks';
import { store as preferencesStore } from '@wordpress/preferences';
import { store as interfaceStore } from '@wordpress/interface';

/**
* Internal dependencies
*/
import { store as editorStore } from '../../store';
import { TEMPLATE_POST_TYPE } from '../../store/constants';

export function useStartPatterns() {
// A pattern is a start pattern if it includes 'core/post-content' in its blockTypes,
// and it has no postTypes declared and the current post type is page or if
// the current post type is part of the postTypes declared.
const { blockPatternsWithPostContentBlockType, postType } = useSelect(
( select ) => {
const { getPatternsByBlockTypes, getBlocksByName } =
select( blockEditorStore );
const { getCurrentPostType, getRenderingMode } =
select( editorStore );
const rootClientId =
getRenderingMode() === 'post-only'
? ''
: getBlocksByName( 'core/post-content' )?.[ 0 ];
return {
blockPatternsWithPostContentBlockType: getPatternsByBlockTypes(
'core/post-content',
rootClientId
),
postType: getCurrentPostType(),
};
},
[]
);

return useMemo( () => {
if ( ! blockPatternsWithPostContentBlockType?.length ) {
return [];
}

/*
* Filter patterns without postTypes declared if the current postType is page
* or patterns that declare the current postType in its post type array.
*/
return blockPatternsWithPostContentBlockType.filter( ( pattern ) => {
return (
( postType === 'page' && ! pattern.postTypes ) ||
( Array.isArray( pattern.postTypes ) &&
pattern.postTypes.includes( postType ) )
);
} );
}, [ postType, blockPatternsWithPostContentBlockType ] );
}

function PatternSelection( { blockPatterns, onChoosePattern } ) {
const { editEntityRecord } = useDispatch( coreStore );
const { postType, postId } = useSelect( ( select ) => {
const { getCurrentPostType, getCurrentPostId } = select( editorStore );

return {
postType: getCurrentPostType(),
postId: getCurrentPostId(),
};
}, [] );
return (
<BlockPatternsList
blockPatterns={ blockPatterns }
onClickPattern={ ( _pattern, blocks ) => {
editEntityRecord( 'postType', postType, postId, {
blocks,
content: ( { blocks: blocksForSerialization = [] } ) =>
__unstableSerializeAndClean( blocksForSerialization ),
} );
onChoosePattern();
} }
/>
);
}

function StartPageOptionsModal( { onClose } ) {
const startPatterns = useStartPatterns();
const hasStartPattern = startPatterns.length > 0;

if ( ! hasStartPattern ) {
return null;
}

return (
<Modal
title={ __( 'Choose a pattern' ) }
isFullScreen
onRequestClose={ onClose }
>
<div className="editor-start-page-options__modal-content">
<PatternSelection
blockPatterns={ startPatterns }
onChoosePattern={ onClose }
/>
</div>
</Modal>
);
}

export default function StartPageOptions() {
const [ isClosed, setIsClosed ] = useState( false );
const shouldEnableModal = useSelect( ( select ) => {
const { isEditedPostDirty, isEditedPostEmpty, getCurrentPostType } =
select( editorStore );
const { postId, shouldEnable } = useSelect( ( select ) => {
const {
isEditedPostDirty,
isEditedPostEmpty,
getCurrentPostId,
getCurrentPostType,
} = select( editorStore );
const preferencesModalActive =
select( interfaceStore ).isModalActive( 'editor/preferences' );
const choosePatternModalEnabled = select( preferencesStore ).get(
'core',
'enableChoosePatternModal'
);
return (
choosePatternModalEnabled &&
! preferencesModalActive &&
! isEditedPostDirty() &&
isEditedPostEmpty() &&
TEMPLATE_POST_TYPE !== getCurrentPostType()
);
return {
postId: getCurrentPostId(),
shouldEnable:
choosePatternModalEnabled &&
! preferencesModalActive &&
! isEditedPostDirty() &&
isEditedPostEmpty() &&
'page' === getCurrentPostType(),
};
}, [] );
const { setIsInserterOpened } = useDispatch( editorStore );

useEffect( () => {
if ( shouldEnable ) {
setIsInserterOpened( {
tab: 'patterns',
category: 'core/starter-content',
} );
}
}, [ postId, shouldEnable, setIsInserterOpened ] );

if ( ! shouldEnableModal || isClosed ) {
return null;
}

return <StartPageOptionsModal onClose={ () => setIsClosed( true ) } />;
return null;
}
3 changes: 0 additions & 3 deletions test/e2e/specs/editor/blocks/image.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -424,9 +424,6 @@ test.describe( 'Image', () => {
page,
editor,
} ) => {
// This is a temp workaround for dragging and dropping images from the inserter.
// This should be removed when we have the zoom out view for media categories.
await page.setViewportSize( { width: 1400, height: 800 } );
await editor.insertBlock( { name: 'core/image' } );
const imageBlock = editor.canvas.getByRole( 'document', {
name: 'Block: Image',
Expand Down
4 changes: 4 additions & 0 deletions test/e2e/specs/editor/various/template-resolution.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,15 @@ test.describe( 'Template resolution', () => {
status: 'publish',
} );
await admin.editPost( newPage.id );
await page.locator( 'role=button[name="Block Inserter"i]' ).click();
await editor.openDocumentSettingsSidebar();
await expect(
page.getByRole( 'button', { name: 'Template options' } )
).toHaveText( 'Single Entries' );
await updateSiteSettings( { requestUtils, pageId: newPage.id } );
await page.reload();
await page.locator( 'role=button[name="Block Inserter"i]' ).click();
await editor.openDocumentSettingsSidebar();
await expect(
page.getByRole( 'button', { name: 'Template options' } )
).toHaveText( 'Index' );
Expand All @@ -81,6 +84,7 @@ test.describe( 'Template resolution', () => {
postType: 'page',
canvas: 'edit',
} );
await page.locator( 'role=button[name="Block Inserter"i]' ).click();
await editor.openDocumentSettingsSidebar();
await expect(
page.getByRole( 'button', { name: 'Template options' } )
Expand Down
4 changes: 1 addition & 3 deletions test/e2e/specs/site-editor/block-style-variations.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,7 @@ async function draftNewPage( page ) {

// Create a Group block with 2 nested Group blocks.
async function addPageContent( editor, page ) {
const inserterButton = page.locator(
'role=button[name="Block Inserter"i]'
);
const inserterButton = page.locator( 'role=tab[name="Blocks"i]' );
await inserterButton.click();
await page.type( 'role=searchbox[name="Search"i]', 'Group' );
await page.click(
Expand Down
Loading

1 comment on commit 1185005

@github-actions
Copy link

Choose a reason for hiding this comment

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

Flaky tests detected in 1185005.
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/12433111235
📝 Reported issues:

Please sign in to comment.