Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract pattern-overrides code from the block-editor package #60887

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 6 additions & 13 deletions packages/block-editor/src/components/block-rename/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Button,
TextControl,
Modal,
__experimentalText as Text,
} from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { useState, useId } from '@wordpress/element';
Expand All @@ -20,11 +21,9 @@ import isEmptyString from './is-empty-string';
export default function BlockRenameModal( {
blockName,
originalBlockName,
helpText,
onClose,
onSave,
// Pattern Overrides is a WordPress-only feature but it also uses the Block Binding API.
// Ideally this should not be inside the block editor package, but we keep it here for simplicity.
hasOverridesWarning,
} ) {
const [ editedBlockName, setEditedBlockName ] = useState( blockName );
const descriptionId = useId();
Expand Down Expand Up @@ -79,23 +78,17 @@ export default function BlockRenameModal( {
handleSubmit();
} }
>
<p id={ descriptionId }>
{ __( 'Enter a custom name for this block.' ) }
</p>
<VStack spacing="3">
<Text id={ descriptionId }>
{ __( 'Enter a custom name for this block.' ) }
</Text>
<TextControl
__nextHasNoMarginBottom
__next40pxDefaultSize
value={ editedBlockName }
label={ __( 'Block name' ) }
help={ helpText }
hideLabelFromVision
help={
hasOverridesWarning
? __(
'This block allows overrides. Changing the name can cause problems with content entered into instances of this pattern.'
)
: undefined
}
placeholder={ originalBlockName }
onChange={ setEditedBlockName }
onFocus={ autoSelectInputText }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useState } from '@wordpress/element';
/**
* Internal dependencies
*/
import { unlock } from '../../lock-unlock';
import { store as blockEditorStore } from '../../store';
import { useBlockDisplayInformation } from '..';
import isEmptyString from './is-empty-string';
Expand All @@ -17,13 +18,24 @@ import BlockRenameModal from './modal';
export default function BlockRenameControl( { clientId } ) {
const [ renamingBlock, setRenamingBlock ] = useState( false );

const { metadata } = useSelect(
const { metadata, onRenameBlock } = useSelect(
( select ) => {
const { getBlockAttributes } = select( blockEditorStore );
const { getBlockAttributes, getSettings } =
select( blockEditorStore );
const settings = getSettings();
// Try unlocking the settings if it has been locked, otherwise renameBlock is not set.
let renameBlock;
try {
renameBlock = unlock( settings ).renameBlock;
} catch ( err ) {
renameBlock = undefined;
}

const _metadata = getBlockAttributes( clientId )?.metadata;
return {
metadata: _metadata,
// Custom extended rename block function.
onRenameBlock: renameBlock,
};
},
[ clientId ]
Expand All @@ -32,12 +44,6 @@ export default function BlockRenameControl( { clientId } ) {
const { updateBlockAttributes } = useDispatch( blockEditorStore );

const customName = metadata?.name;
const hasPatternOverrides =
!! customName &&
!! metadata?.bindings &&
Object.values( metadata.bindings ).some(
( binding ) => binding.source === 'core/pattern-overrides'
);

function onChange( newName ) {
updateBlockAttributes( [ clientId ], {
Expand All @@ -54,9 +60,12 @@ export default function BlockRenameControl( { clientId } ) {
<>
<MenuItem
onClick={ () => {
setRenamingBlock( true );
if ( onRenameBlock ) {
onRenameBlock( clientId );
} else {
setRenamingBlock( true );
}
} }
aria-expanded={ renamingBlock }
Copy link
Member Author

Choose a reason for hiding this comment

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

Since we're opening a dialog and we won't be able to access this element when the dialog is open, I don't see why we need this 🤔. I could be wrong though so could use a sanity check from @Mamaduka. 🙏

Copy link
Member

Choose a reason for hiding this comment

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

You're right. The content behind the dialog become "inert" and browser should ignore them. Adding them to few menu items with modals was my mistake.

aria-haspopup="dialog"
>
{ __( 'Rename' ) }
Expand All @@ -65,7 +74,6 @@ export default function BlockRenameControl( { clientId } ) {
<BlockRenameModal
blockName={ customName || '' }
originalBlockName={ blockInformation?.title }
hasOverridesWarning={ hasPatternOverrides }
onClose={ () => setRenamingBlock( false ) }
onSave={ ( newName ) => {
// If the new value is the block's original name (e.g. `Group`)
Expand Down
2 changes: 2 additions & 0 deletions packages/block-editor/src/private-apis.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
} from './store/private-keys';
import { requiresWrapperOnCopy } from './components/writing-flow/utils';
import { PrivateRichText } from './components/rich-text/';
import { BlockRenameModal } from './components/block-rename';

/**
* Private @wordpress/block-editor APIs.
Expand Down Expand Up @@ -74,4 +75,5 @@ lock( privateApis, {
requiresWrapperOnCopy,
PrivateRichText,
reusableBlocksSelectKey,
BlockRenameModal,
} );
5 changes: 4 additions & 1 deletion packages/editor/src/components/provider/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ import StartPageOptions from '../start-page-options';
import KeyboardShortcutHelpModal from '../keyboard-shortcut-help-modal';

const { ExperimentalBlockEditorProvider } = unlock( blockEditorPrivateApis );
const { PatternsMenuItems } = unlock( editPatternsPrivateApis );
const { PatternsMenuItems, RenameBlockModalControl } = unlock(
editPatternsPrivateApis
);

const noop = () => {};

Expand Down Expand Up @@ -264,6 +266,7 @@ export const ExperimentalEditorProvider = withRegistryProvider(
{ ! settings.__unstableIsPreviewMode && (
<>
<PatternsMenuItems />
<RenameBlockModalControl />
{ mode === 'template-locked' && (
<DisableNonPageContentBlocks />
) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
privateApis,
store as blockEditorStore,
} from '@wordpress/block-editor';
import { store as patternsStore } from '@wordpress/patterns';

/**
* Internal dependencies
Expand Down Expand Up @@ -209,6 +210,7 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) {
const { undo, setIsInserterOpened } = useDispatch( editorStore );

const { saveEntityRecord } = useDispatch( coreStore );
const { setRenamingBlock } = unlock( useDispatch( patternsStore ) );

/**
* Creates a Post entity.
Expand Down Expand Up @@ -303,6 +305,7 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) {
};
lock( blockEditorSettings, {
sectionRootClientId,
renameBlock: setRenamingBlock,
} );
return blockEditorSettings;
}, [
Expand All @@ -327,6 +330,7 @@ function useBlockEditorSettings( settings, postType, postId, renderingMode ) {
postType,
setIsInserterOpened,
sectionRootClientId,
setRenamingBlock,
] );
}

Expand Down
90 changes: 90 additions & 0 deletions packages/patterns/src/components/rename-block-modal-control.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { useSelect, useDispatch } from '@wordpress/data';
import {
store as blockEditorStore,
useBlockDisplayInformation,
privateApis as blockEditorPrivateApis,
} from '@wordpress/block-editor';

/**
* Internal dependencies
*/
import { store as patternsStore } from '../store';
import { unlock } from '../lock-unlock';
import { PATTERN_OVERRIDES_BINDING_SOURCE } from '../constants';

const { BlockRenameModal } = unlock( blockEditorPrivateApis );

function BlockRenameModalWrapper( { clientId } ) {
const { metadata } = useSelect(
( select ) => {
const { getBlockAttributes } = select( blockEditorStore );
const attributes = getBlockAttributes( clientId );
return {
metadata: attributes?.metadata,
};
},
[ clientId ]
);
const { updateBlockAttributes } = useDispatch( blockEditorStore );
const { setRenamingBlock } = unlock( useDispatch( patternsStore ) );
const blockInformation = useBlockDisplayInformation( clientId );
const customName = metadata?.name ?? '';
const hasPatternOverrides =
!! customName &&
!! metadata?.bindings &&
Object.values( metadata.bindings ).some(
( binding ) => binding.source === PATTERN_OVERRIDES_BINDING_SOURCE
);

const closeModal = () => setRenamingBlock( null );
const onRename = ( newName ) => {
// If the new value is the block's original name (e.g. `Group`)
// or it is an empty string then assume the intent is to reset
// the value. Therefore reset the metadata.
if ( newName === blockInformation?.title || ! newName.trim() ) {
newName = undefined;
}

updateBlockAttributes( [ clientId ], {
metadata: {
...metadata,
name: newName,
},
} );
};

return (
<BlockRenameModal
blockName={ customName }
originalBlockName={ blockInformation?.title }
helpText={
hasPatternOverrides
? __(
'This block allows overrides. Changing the name can cause problems with content entered into instances of this pattern.'
)
: undefined
}
onClose={ closeModal }
onSave={ onRename }
/>
);
}

// Split into a different component to minimize the store subscriptions.
export default function RenameBlockModalControl() {
const { renamingBlockClientId } = useSelect(
( select ) => ( {
renamingBlockClientId: unlock(
select( patternsStore )
).getRenamingBlockClientId(),
} ),
[]
);
if ( ! renamingBlockClientId ) return null;

return <BlockRenameModalWrapper clientId={ renamingBlockClientId } />;
}
3 changes: 2 additions & 1 deletion packages/patterns/src/index.native.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/**
* Internal dependencies
*/
import './store';
export { store } from './store';
import { lock } from './lock-unlock';

export const privateApis = {};
lock( privateApis, {
CreatePatternModal: () => null,
PatternsMenuItems: () => null,
RenameBlockModalControl: () => null,
} );
2 changes: 2 additions & 0 deletions packages/patterns/src/private-apis.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import PatternsMenuItems from './components';
import RenamePatternCategoryModal from './components/rename-pattern-category-modal';
import PatternOverridesControls from './components/pattern-overrides-controls';
import ResetOverridesControl from './components/reset-overrides-control';
import RenameBlockModalControl from './components/rename-block-modal-control';
import { useAddPatternCategory } from './private-hooks';
import {
PATTERN_TYPES,
Expand All @@ -40,6 +41,7 @@ lock( privateApis, {
RenamePatternCategoryModal,
PatternOverridesControls,
ResetOverridesControl,
RenameBlockModalControl,
useAddPatternCategory,
PATTERN_TYPES,
PATTERN_DEFAULT_CATEGORY,
Expand Down
11 changes: 11 additions & 0 deletions packages/patterns/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,14 @@ export function setEditingPattern( clientId, isEditing ) {
isEditing,
};
}

/**
* Set the renaming block to open the rename modal.
* @param {string|null} clientId The client ID of the block to rename, or `null` to close the modal.
*/
export function setRenamingBlock( clientId ) {
return {
type: 'SET_RENAMING_BLOCK',
clientId,
};
}
9 changes: 9 additions & 0 deletions packages/patterns/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ export function isEditingPattern( state = {}, action ) {
return state;
}

export function renamingBlock( state = null, action ) {
if ( action?.type === 'SET_RENAMING_BLOCK' ) {
return action.clientId;
}

return state;
}

export default combineReducers( {
isEditingPattern,
renamingBlock,
} );
4 changes: 4 additions & 0 deletions packages/patterns/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@
export function isEditingPattern( state, clientId ) {
return state.isEditingPattern[ clientId ];
}

export function getRenamingBlockClientId( state ) {
return state.renamingBlock;
}
9 changes: 2 additions & 7 deletions test/e2e/specs/editor/various/block-renaming.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ test.describe( 'Block Renaming', () => {
} );

await expect( renameMenuItem ).toHaveAttribute(
'aria-expanded',
'true'
'aria-haspopup',
'dialog'
);

const renameModal = page.getByRole( 'dialog', {
Expand Down Expand Up @@ -102,11 +102,6 @@ test.describe( 'Block Renaming', () => {
// Check that focus is transferred back to original "Rename" menu item.
await expect( renameMenuItem ).toBeFocused();

await expect( renameMenuItem ).toHaveAttribute(
'aria-expanded',
'false'
);

// Check custom name reflected in List View.
listView.getByRole( 'link', {
name: 'My new name',
Expand Down
Loading