Skip to content
Merged
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
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { MenuItem } from '@wordpress/components';
import { useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { privateApis as patternsPrivateApis } from '@wordpress/patterns';
/**
* Internal dependencies
*/
import usePatternCategories from '../sidebar-navigation-screen-patterns/use-pattern-categories';

/**
* Internal dependencies
Expand All @@ -16,30 +20,43 @@ const { RenamePatternCategoryModal } = unlock( patternsPrivateApis );
export default function RenameCategoryMenuItem( { category, onClose } ) {
const [ isModalOpen, setIsModalOpen ] = useState( false );

// User created pattern categories have their properties updated when
// retrieved via `getUserPatternCategories`. The rename modal expects an
// object that will match the pattern category entity.
const normalizedCategory = {
id: category.id,
slug: category.slug,
name: category.label,
};

return (
<>
<MenuItem onClick={ () => setIsModalOpen( true ) }>
{ __( 'Rename' ) }
</MenuItem>
{ isModalOpen && (
<RenamePatternCategoryModal
category={ normalizedCategory }
<RenameModal
category={ category }
onClose={ () => {
setIsModalOpen( false );
onClose();
} }
overlayClassName="edit-site-list__rename-modal"
/>
) }
</>
);
}

function RenameModal( { category, onClose } ) {
// User created pattern categories have their properties updated when
// retrieved via `getUserPatternCategories`. The rename modal expects an
// object that will match the pattern category entity.
const normalizedCategory = {
id: category.id,
slug: category.slug,
name: category.label,
};

// Optimization - only use pattern categories when the modal is open.
const existingCategories = usePatternCategories();

return (
<RenamePatternCategoryModal
category={ normalizedCategory }
existingCategories={ existingCategories }
onClose={ onClose }
overlayClassName="edit-site-list__rename-modal"
/>
);
}
1 change: 1 addition & 0 deletions packages/patterns/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
],
"dependencies": {
"@babel/runtime": "^7.16.0",
"@wordpress/a11y": "file:../a11y",
"@wordpress/block-editor": "file:../block-editor",
"@wordpress/blocks": "file:../blocks",
"@wordpress/components": "file:../components",
Expand Down
78 changes: 66 additions & 12 deletions packages/patterns/src/components/rename-pattern-category-modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import {
} from '@wordpress/components';
import { store as coreStore } from '@wordpress/core-data';
import { useDispatch } from '@wordpress/data';
import { useState } from '@wordpress/element';
import { useId, useRef, useState } from '@wordpress/element';
import { decodeEntities } from '@wordpress/html-entities';
import { __ } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';
import { speak } from '@wordpress/a11y';

/**
* Internal dependencies
Expand All @@ -22,23 +23,64 @@ import { CATEGORY_SLUG } from './category-selector';

export default function RenamePatternCategoryModal( {
category,
existingCategories,
onClose,
onError,
onSuccess,
...props
} ) {
const id = useId();
const textControlRef = useRef();
const [ name, setName ] = useState( decodeEntities( category.name ) );
const [ isSaving, setIsSaving ] = useState( false );
const [ validationMessage, setValidationMessage ] = useState( false );
const validationMessageId = validationMessage
? `patterns-rename-pattern-category-modal__validation-message-${ id }`
: undefined;

const { saveEntityRecord, invalidateResolution } = useDispatch( coreStore );

const { createErrorNotice, createSuccessNotice } =
useDispatch( noticesStore );

const onRename = async ( event ) => {
const onChange = ( newName ) => {
if ( validationMessage ) {
setValidationMessage( undefined );
}
setName( newName );
};

const onSave = async ( event ) => {
event.preventDefault();

if ( ! name || name === category.name || isSaving ) {
if ( isSaving ) {
return;
}

if ( ! name || name === category.name ) {
const message = __( 'Please enter a new name for this category.' );
speak( message, 'assertive' );
setValidationMessage( message );
textControlRef.current?.focus();
return;
}

// Check existing categories to avoid creating duplicates.
if (
existingCategories.patternCategories.find( ( existingCategory ) => {
// Compare the id so that the we don't disallow the user changing the case of their current category
// (i.e. renaming 'test' to 'Test').
return (
existingCategory.id !== category.id &&
existingCategory.label.toLowerCase() === name.toLowerCase()
);
} )
) {
const message = __(
'This category already exists. Please use a different name.'
);
speak( message, 'assertive' );
setValidationMessage( message );
textControlRef.current?.focus();
return;
}

Expand Down Expand Up @@ -90,15 +132,27 @@ export default function RenamePatternCategoryModal( {
onRequestClose={ onRequestClose }
{ ...props }
>
<form onSubmit={ onRename }>
<form onSubmit={ onSave }>
<VStack spacing="5">
<TextControl
__nextHasNoMarginBottom
label={ __( 'Name' ) }
value={ name }
onChange={ setName }
required
/>
<VStack spacing="2">
<TextControl
ref={ textControlRef }
__nextHasNoMarginBottom
label={ __( 'Name' ) }
value={ name }
onChange={ onChange }
aria-describedby={ validationMessageId }
required
/>
{ validationMessage && (
<span
className="patterns-rename-pattern-category-modal__validation-message"
id={ validationMessageId }
>
{ validationMessage }
</span>
) }
</VStack>
<HStack justify="right">
<Button variant="tertiary" onClick={ onRequestClose }>
{ __( 'Cancel' ) }
Expand Down
9 changes: 9 additions & 0 deletions packages/patterns/src/components/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,12 @@
// Override the default 1px margin-x.
margin: 0;
}

.patterns-rename-pattern-category-modal__validation-message {
color: $alert-red;

// Match the input size.
@include break-medium() {
width: $grid-unit * 40;
}
}