-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Patterns: Add rename/delete options for pattern categories in site ed…
…itor (#55035)
- Loading branch information
1 parent
68a33b5
commit ef21f20
Showing
7 changed files
with
324 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 104 additions & 0 deletions
104
packages/edit-site/src/components/page-patterns/delete-category-menu-item.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { | ||
MenuItem, | ||
__experimentalConfirmDialog as ConfirmDialog, | ||
} from '@wordpress/components'; | ||
import { store as coreStore } from '@wordpress/core-data'; | ||
import { useDispatch } from '@wordpress/data'; | ||
import { useState } from '@wordpress/element'; | ||
import { decodeEntities } from '@wordpress/html-entities'; | ||
import { __, sprintf } from '@wordpress/i18n'; | ||
import { store as noticesStore } from '@wordpress/notices'; | ||
import { privateApis as routerPrivateApis } from '@wordpress/router'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { unlock } from '../../lock-unlock'; | ||
import { PATTERN_TYPES, PATTERN_DEFAULT_CATEGORY } from '../../utils/constants'; | ||
|
||
const { useHistory } = unlock( routerPrivateApis ); | ||
|
||
export default function DeleteCategoryMenuItem( { category, onClose } ) { | ||
const [ isModalOpen, setIsModalOpen ] = useState( false ); | ||
const history = useHistory(); | ||
|
||
const { createSuccessNotice, createErrorNotice } = | ||
useDispatch( noticesStore ); | ||
const { deleteEntityRecord, invalidateResolution } = | ||
useDispatch( coreStore ); | ||
|
||
const onDelete = async () => { | ||
try { | ||
await deleteEntityRecord( | ||
'taxonomy', | ||
'wp_pattern_category', | ||
category.id, | ||
{ force: true }, | ||
{ throwOnError: true } | ||
); | ||
|
||
// Prevent the need to refresh the page to get up-to-date categories | ||
// and pattern categorization. | ||
invalidateResolution( 'getUserPatternCategories' ); | ||
invalidateResolution( 'getEntityRecords', [ | ||
'postType', | ||
PATTERN_TYPES.user, | ||
{ per_page: -1 }, | ||
] ); | ||
|
||
createSuccessNotice( | ||
sprintf( | ||
/* translators: The pattern category's name */ | ||
__( '"%s" deleted.' ), | ||
category.label | ||
), | ||
{ type: 'snackbar', id: 'pattern-category-delete' } | ||
); | ||
|
||
onClose?.(); | ||
history.push( { | ||
path: `/patterns`, | ||
categoryType: PATTERN_TYPES.theme, | ||
categoryId: PATTERN_DEFAULT_CATEGORY, | ||
} ); | ||
} catch ( error ) { | ||
const errorMessage = | ||
error.message && error.code !== 'unknown_error' | ||
? error.message | ||
: __( | ||
'An error occurred while deleting the pattern category.' | ||
); | ||
|
||
createErrorNotice( errorMessage, { | ||
type: 'snackbar', | ||
id: 'pattern-category-delete', | ||
} ); | ||
} | ||
}; | ||
|
||
return ( | ||
<> | ||
<MenuItem isDestructive onClick={ () => setIsModalOpen( true ) }> | ||
{ __( 'Delete' ) } | ||
</MenuItem> | ||
<ConfirmDialog | ||
isOpen={ isModalOpen } | ||
onConfirm={ onDelete } | ||
onCancel={ () => setIsModalOpen( false ) } | ||
confirmButtonText={ __( 'Delete' ) } | ||
className="edit-site-patterns__delete-modal" | ||
> | ||
{ sprintf( | ||
// translators: %s: The pattern category's name. | ||
__( | ||
'Are you sure you want to delete the category "%s"? The patterns will not be deleted.' | ||
), | ||
decodeEntities( category.label ) | ||
) } | ||
</ConfirmDialog> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
packages/edit-site/src/components/page-patterns/rename-category-menu-item.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { MenuItem } from '@wordpress/components'; | ||
import { useState } from '@wordpress/element'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { privateApis as patternsPrivateApis } from '@wordpress/patterns'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { unlock } from '../../lock-unlock'; | ||
|
||
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 } | ||
onClose={ () => { | ||
setIsModalOpen( false ); | ||
onClose(); | ||
} } | ||
overlayClassName="edit-site-list__rename-modal" | ||
/> | ||
) } | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
121 changes: 121 additions & 0 deletions
121
packages/patterns/src/components/rename-pattern-category-modal.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { | ||
Modal, | ||
Button, | ||
TextControl, | ||
__experimentalHStack as HStack, | ||
__experimentalVStack as VStack, | ||
} from '@wordpress/components'; | ||
import { store as coreStore } from '@wordpress/core-data'; | ||
import { useDispatch } from '@wordpress/data'; | ||
import { useState } from '@wordpress/element'; | ||
import { decodeEntities } from '@wordpress/html-entities'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { store as noticesStore } from '@wordpress/notices'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { CATEGORY_SLUG } from './category-selector'; | ||
|
||
export default function RenamePatternCategoryModal( { | ||
category, | ||
onClose, | ||
onError, | ||
onSuccess, | ||
...props | ||
} ) { | ||
const [ name, setName ] = useState( decodeEntities( category.name ) ); | ||
const [ isSaving, setIsSaving ] = useState( false ); | ||
|
||
const { saveEntityRecord, invalidateResolution } = useDispatch( coreStore ); | ||
|
||
const { createErrorNotice, createSuccessNotice } = | ||
useDispatch( noticesStore ); | ||
|
||
const onRename = async ( event ) => { | ||
event.preventDefault(); | ||
|
||
if ( ! name || name === category.name || isSaving ) { | ||
return; | ||
} | ||
|
||
try { | ||
setIsSaving( true ); | ||
|
||
// User pattern category properties may differ as they can be | ||
// normalized for use alongside template part areas, core pattern | ||
// categories etc. As a result we won't just destructure the passed | ||
// category object. | ||
const savedRecord = await saveEntityRecord( | ||
'taxonomy', | ||
CATEGORY_SLUG, | ||
{ | ||
id: category.id, | ||
slug: category.slug, | ||
name, | ||
} | ||
); | ||
|
||
invalidateResolution( 'getUserPatternCategories' ); | ||
onSuccess?.( savedRecord ); | ||
onClose(); | ||
|
||
createSuccessNotice( __( 'Pattern category renamed.' ), { | ||
type: 'snackbar', | ||
id: 'pattern-category-update', | ||
} ); | ||
} catch ( error ) { | ||
onError?.(); | ||
createErrorNotice( error.message, { | ||
type: 'snackbar', | ||
id: 'pattern-category-update', | ||
} ); | ||
} finally { | ||
setIsSaving( false ); | ||
setName( '' ); | ||
} | ||
}; | ||
|
||
const onRequestClose = () => { | ||
onClose(); | ||
setName( '' ); | ||
}; | ||
|
||
return ( | ||
<Modal | ||
title={ __( 'Rename' ) } | ||
onRequestClose={ onRequestClose } | ||
{ ...props } | ||
> | ||
<form onSubmit={ onRename }> | ||
<VStack spacing="5"> | ||
<TextControl | ||
__nextHasNoMarginBottom | ||
label={ __( 'Name' ) } | ||
value={ name } | ||
onChange={ setName } | ||
required | ||
/> | ||
<HStack justify="right"> | ||
<Button variant="tertiary" onClick={ onRequestClose }> | ||
{ __( 'Cancel' ) } | ||
</Button> | ||
<Button | ||
variant="primary" | ||
type="submit" | ||
aria-disabled={ | ||
! name || name === category.name || isSaving | ||
} | ||
isBusy={ isSaving } | ||
> | ||
{ __( 'Save' ) } | ||
</Button> | ||
</HStack> | ||
</VStack> | ||
</form> | ||
</Modal> | ||
); | ||
} |
Oops, something went wrong.