Skip to content

Commit

Permalink
Backmerge block renaming fixes/refactors from 6.4 branch into Gutenbe…
Browse files Browse the repository at this point in the history
…rg trunk (#56386)

* Apply refactor from 6.4 branch

* Apply fix to lock down rename

* Update support hook to check correct support value

* Rename hook to match feature

* Correct test name

* Assert against a Block that doesn’t support the feature

* Ensure presence of options menu before Rename presence assertion

* Avoid redundant creation of blocks and use Group directly

* Add additional test coverage for options menu on unselected blocks

* Update hook to check for correct support
  • Loading branch information
getdave authored Nov 24, 2023
1 parent 1fb0d7b commit a9720a0
Show file tree
Hide file tree
Showing 12 changed files with 376 additions and 272 deletions.
3 changes: 3 additions & 0 deletions packages/block-editor/src/components/block-rename/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as BlockRenameControl } from './rename-control';
export { default as BlockRenameModal } from './modal';
export { default as useBlockRename } from './use-block-rename';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function isEmptyString( testString ) {
return testString?.trim()?.length === 0;
}
115 changes: 115 additions & 0 deletions packages/block-editor/src/components/block-rename/modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* WordPress dependencies
*/
import {
__experimentalHStack as HStack,
__experimentalVStack as VStack,
Button,
TextControl,
Modal,
} from '@wordpress/components';
import { useInstanceId } from '@wordpress/compose';
import { __, sprintf } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { speak } from '@wordpress/a11y';

/**
* Internal dependencies
*/
import isEmptyString from './is-empty-string';

export default function BlockRenameModal( {
blockName,
originalBlockName,
onClose,
onSave,
} ) {
const [ editedBlockName, setEditedBlockName ] = useState( blockName );

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

const isNameValid = nameHasChanged || nameIsOriginal;

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

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

const handleSubmit = () => {
const message =
nameIsOriginal || nameIsEmpty
? sprintf(
/* translators: %s: new name/label for the block */
__( 'Block name reset to: "%s".' ),
editedBlockName
)
: sprintf(
/* translators: %s: new name/label for the block */
__( 'Block name changed to: "%s".' ),
editedBlockName
);

// Must be assertive to immediately announce change.
speak( message, 'assertive' );
onSave( editedBlockName );

// Immediate close avoids ability to hit save multiple times.
onClose();
};

return (
<Modal
title={ __( 'Rename' ) }
onRequestClose={ onClose }
overlayClassName="block-editor-block-rename-modal"
aria={ {
describedby: dialogDescription,
} }
focusOnMount="firstContentElement"
>
<p id={ dialogDescription }>
{ __( 'Enter a custom name for this block.' ) }
</p>
<form
onSubmit={ ( e ) => {
e.preventDefault();

if ( ! isNameValid ) {
return;
}

handleSubmit();
} }
>
<VStack spacing="3">
<TextControl
__nextHasNoMarginBottom
value={ editedBlockName }
label={ __( 'Block name' ) }
hideLabelFromVision={ true }
placeholder={ originalBlockName }
onChange={ setEditedBlockName }
onFocus={ autoSelectInputText }
/>
<HStack justify="right">
<Button variant="tertiary" onClick={ onClose }>
{ __( 'Cancel' ) }
</Button>

<Button
aria-disabled={ ! isNameValid }
variant="primary"
type="submit"
>
{ __( 'Save' ) }
</Button>
</HStack>
</VStack>
</form>
</Modal>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* WordPress dependencies
*/
import { MenuItem } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';

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

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

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

const _metadata = getBlockAttributes( clientId )?.metadata;
return {
metadata: _metadata,
};
},
[ clientId ]
);

const { updateBlockAttributes } = useDispatch( blockEditorStore );

const customName = metadata?.name;

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

const blockInformation = useBlockDisplayInformation( clientId );

return (
<>
<MenuItem
onClick={ () => {
setRenamingBlock( true );
} }
aria-expanded={ renamingBlock }
aria-haspopup="dialog"
>
{ __( 'Rename' ) }
</MenuItem>
{ renamingBlock && (
<BlockRenameModal
blockName={ customName || '' }
originalBlockName={ blockInformation?.title }
onClose={ () => setRenamingBlock( false ) }
onSave={ ( 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 ||
isEmptyString( newName )
) {
newName = undefined;
}

onChange( newName );
} }
/>
) }
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* WordPress dependencies
*/
import { getBlockSupport } from '@wordpress/blocks';

export default function useBlockRename( name ) {
return {
canRename: getBlockSupport( name, 'renaming', true ),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { BlockLockMenuItem, useBlockLock } from '../block-lock';
import { store as blockEditorStore } from '../../store';
import BlockModeToggle from '../block-settings-menu/block-mode-toggle';

import { BlockRenameControl, useBlockRename } from '../block-rename';

const { Fill, Slot } = createSlotFill( 'BlockSettingsMenuControls' );

const BlockSettingsMenuControlsSlot = ( {
Expand All @@ -44,7 +46,9 @@ const BlockSettingsMenuControlsSlot = ( {
);

const { canLock } = useBlockLock( selectedClientIds[ 0 ] );
const { canRename } = useBlockRename( selectedBlocks[ 0 ] );
const showLockButton = selectedClientIds.length === 1 && canLock;
const showRenameButton = selectedClientIds.length === 1 && canRename;

// Check if current selection of blocks is Groupable or Ungroupable
// and pass this props down to ConvertToGroupButton.
Expand Down Expand Up @@ -84,6 +88,11 @@ const BlockSettingsMenuControlsSlot = ( {
clientId={ selectedClientIds[ 0 ] }
/>
) }
{ showRenameButton && (
<BlockRenameControl
clientId={ selectedClientIds[ 0 ] }
/>
) }
{ fills }
{ fillProps?.canMove && ! fillProps?.onlyBlock && (
<MenuItem
Expand Down
Loading

0 comments on commit a9720a0

Please sign in to comment.