Skip to content

Commit

Permalink
Refine renaming flow for blocks with overrides
Browse files Browse the repository at this point in the history
  • Loading branch information
kevin940726 committed Apr 3, 2024
1 parent 910f598 commit 5e8be30
Show file tree
Hide file tree
Showing 17 changed files with 383 additions and 362 deletions.
65 changes: 48 additions & 17 deletions packages/block-editor/src/components/block-rename/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,49 @@ import {
Button,
TextControl,
Modal,
ToggleControl,
} from '@wordpress/components';
import { useInstanceId } from '@wordpress/compose';
import { __, sprintf } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { speak } from '@wordpress/a11y';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import isEmptyString from './is-empty-string';
import { store as blockEditorStore } from '../../store';

export default function BlockRenameModal( {
blockName,
originalBlockName,
onClose,
onSave,
initialAllowOverrides,
} ) {
const [ editedBlockName, setEditedBlockName ] = useState( blockName );
const [ allowOverrides, setAllowOverrides ] = useState(
initialAllowOverrides
);
const { isEditingPattern } = useSelect(
( select ) => ( {
isEditingPattern:
select( blockEditorStore ).getSettings()?.postType ===
'wp_block',
} ),
[]
);

const nameHasChanged = editedBlockName !== blockName;
const nameIsOriginal = editedBlockName === originalBlockName;
const nameIsEmpty = isEmptyString( editedBlockName );

const isNameValid = nameHasChanged || nameIsOriginal;
const isSaveDisabled =
! isNameValid && allowOverrides === initialAllowOverrides;

const autoSelectInputText = ( event ) => event.target.select();

const dialogDescription = useInstanceId(
BlockRenameModal,
`block-editor-rename-modal__description`
);

const handleSubmit = () => {
const message =
nameIsOriginal || nameIsEmpty
Expand All @@ -55,7 +66,10 @@ export default function BlockRenameModal( {

// Must be assertive to immediately announce change.
speak( message, 'assertive' );
onSave( editedBlockName );
onSave(
editedBlockName,
isEditingPattern ? allowOverrides : undefined
);

// Immediate close avoids ability to hit save multiple times.
onClose();
Expand All @@ -66,19 +80,14 @@ export default function BlockRenameModal( {
title={ __( 'Rename' ) }
onRequestClose={ onClose }
overlayClassName="block-editor-block-rename-modal"
aria={ {
describedby: dialogDescription,
} }
focusOnMount="firstContentElement"
size="small"
>
<p id={ dialogDescription }>
{ __( 'Enter a custom name for this block.' ) }
</p>
<form
onSubmit={ ( e ) => {
e.preventDefault();

if ( ! isNameValid ) {
if ( isSaveDisabled ) {
return;
}

Expand All @@ -91,11 +100,33 @@ export default function BlockRenameModal( {
__next40pxDefaultSize
value={ editedBlockName }
label={ __( 'Block name' ) }
hideLabelFromVision
help={ __(
'Naming your block will help people understand its purpose.'
) }
placeholder={ originalBlockName }
onChange={ setEditedBlockName }
onChange={ ( newName ) => {
setEditedBlockName( newName );
if ( newName === '' ) {
setAllowOverrides( false );
}
} }
onFocus={ autoSelectInputText }
/>
{ isEditingPattern ? (
<ToggleControl
label={ __( 'Allow overrides' ) }
help={ __(
'Allow overriding this block in instances where this pattern is used.'
) }
checked={ allowOverrides }
onChange={ ( checked ) => {
if ( ! nameIsEmpty ) {
setAllowOverrides( checked );
}
} }
disabled={ nameIsEmpty }
/>
) : null }
<HStack justify="right">
<Button
__next40pxDefaultSize
Expand All @@ -107,7 +138,7 @@ export default function BlockRenameModal( {

<Button
__next40pxDefaultSize
aria-disabled={ ! isNameValid }
aria-disabled={ isSaveDisabled }
variant="primary"
type="submit"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,25 @@ import { store as blockEditorStore } from '../../store';
import { useBlockDisplayInformation } from '..';
import isEmptyString from './is-empty-string';
import BlockRenameModal from './modal';
import {
BLOCK_BINDINGS_ALLOWED_BLOCKS,
canBindBlock,
addBindings,
removeBindings,
} from '../../hooks/use-bindings-attributes';

export default function BlockRenameControl( { clientId } ) {
const [ renamingBlock, setRenamingBlock ] = useState( false );

const { metadata } = useSelect(
const { metadata, blockName } = useSelect(
( select ) => {
const { getBlockAttributes } = select( blockEditorStore );
const { getBlockAttributes, getBlockName } =
select( blockEditorStore );

const _metadata = getBlockAttributes( clientId )?.metadata;
return {
metadata: _metadata,
blockName: getBlockName( clientId ),
};
},
[ clientId ]
Expand All @@ -32,13 +40,33 @@ export default function BlockRenameControl( { clientId } ) {
const { updateBlockAttributes } = useDispatch( blockEditorStore );

const customName = metadata?.name;
const hasPatternOverrides =
!! metadata?.bindings &&
!! BLOCK_BINDINGS_ALLOWED_BLOCKS[ blockName ]?.every(
( attributeName ) =>
metadata.bindings[ attributeName ]?.source ===
'core/pattern-overrides'
);

function onChange( newName, allowOverrides ) {
const updatedMetadata = { ...metadata, name: newName };
if ( allowOverrides !== undefined && canBindBlock( blockName ) ) {
const syncedAttributes = BLOCK_BINDINGS_ALLOWED_BLOCKS[ blockName ];
updatedMetadata.bindings = allowOverrides
? addBindings(
updatedMetadata.bindings,
syncedAttributes,
'core/pattern-overrides'
)
: removeBindings(
updatedMetadata.bindings,
syncedAttributes,
'core/pattern-overrides'
);
}

function onChange( newName ) {
updateBlockAttributes( [ clientId ], {
metadata: {
...( metadata && metadata ),
name: newName,
},
metadata: updatedMetadata,
} );
}

Expand All @@ -59,8 +87,9 @@ export default function BlockRenameControl( { clientId } ) {
<BlockRenameModal
blockName={ customName || '' }
originalBlockName={ blockInformation?.title }
initialAllowOverrides={ hasPatternOverrides }
onClose={ () => setRenamingBlock( false ) }
onSave={ ( newName ) => {
onSave={ ( newName, allowOverrides ) => {
// 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.
Expand All @@ -71,7 +100,7 @@ export default function BlockRenameControl( { clientId } ) {
newName = undefined;
}

onChange( newName );
onChange( newName, allowOverrides );
} }
/>
) }
Expand Down
78 changes: 0 additions & 78 deletions packages/block-editor/src/hooks/block-renaming.js

This file was deleted.

2 changes: 0 additions & 2 deletions packages/block-editor/src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import childLayout from './layout-child';
import contentLockUI from './content-lock-ui';
import './metadata';
import blockHooks from './block-hooks';
import blockRenaming from './block-renaming';
import './use-bindings-attributes';

createBlockEditFilter(
Expand All @@ -42,7 +41,6 @@ createBlockEditFilter(
layout,
contentLockUI,
blockHooks,
blockRenaming,
childLayout,
].filter( Boolean )
);
Expand Down
31 changes: 30 additions & 1 deletion packages/block-editor/src/hooks/use-bindings-attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { unlock } from '../lock-unlock';
* @return {WPHigherOrderComponent} Higher-order component.
*/

const BLOCK_BINDINGS_ALLOWED_BLOCKS = {
export const BLOCK_BINDINGS_ALLOWED_BLOCKS = {
'core/paragraph': [ 'content' ],
'core/heading': [ 'content' ],
'core/image': [ 'url', 'title', 'alt' ],
Expand Down Expand Up @@ -56,6 +56,35 @@ export function canBindAttribute( blockName, attributeName ) {
);
}

export function removeBindings( bindings, syncedAttributes, source ) {
let updatedBindings = {};
for ( const attributeName of syncedAttributes ) {
// Omit any bindings that's not the same source from the `updatedBindings` object.
if (
bindings?.[ attributeName ]?.source !== source &&
bindings?.[ attributeName ]?.source !== undefined
) {
updatedBindings[ attributeName ] = bindings[ attributeName ];
}
}
if ( ! Object.keys( updatedBindings ).length ) {
updatedBindings = undefined;
}
return updatedBindings;
}

export function addBindings( bindings, syncedAttributes, source ) {
const updatedBindings = { ...bindings };
for ( const attributeName of syncedAttributes ) {
if ( ! bindings?.[ attributeName ] ) {
updatedBindings[ attributeName ] = {
source,
};
}
}
return updatedBindings;
}

/**
* This component is responsible for detecting and
* propagating data changes from the source to the block.
Expand Down
3 changes: 3 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 { addBindings, removeBindings } from './hooks/use-bindings-attributes';

/**
* Private @wordpress/block-editor APIs.
Expand Down Expand Up @@ -74,4 +75,6 @@ lock( privateApis, {
requiresWrapperOnCopy,
PrivateRichText,
reusableBlocksSelectKey,
addBindings,
removeBindings,
} );
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ function useBlockEditorSettings( settings, postType, postId ) {
? [ [ 'core/navigation', {}, [] ] ]
: settings.template,
__experimentalSetIsInserterOpened: setIsInserterOpened,
postType,
} ),
[
allowedBlockTypes,
Expand Down
Loading

0 comments on commit 5e8be30

Please sign in to comment.