diff --git a/packages/edit-site/CHANGELOG.md b/packages/edit-site/CHANGELOG.md index 27e12aef878f8d..f5c98f6aea0285 100644 --- a/packages/edit-site/CHANGELOG.md +++ b/packages/edit-site/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Code Quality + +- Migrate the registered-template duplicate dialog in Page Templates from `@wordpress/components` `Modal` to `@wordpress/ui` `Dialog`. ([#76837](https://github.com/WordPress/gutenberg/pull/76837)) + ## 6.45.0 (2026-04-29) ## 6.44.0 (2026-04-15) diff --git a/packages/edit-site/src/components/page-templates/index.js b/packages/edit-site/src/components/page-templates/index.js index e019bf290b39dd..a48885806e4514 100644 --- a/packages/edit-site/src/components/page-templates/index.js +++ b/packages/edit-site/src/components/page-templates/index.js @@ -16,7 +16,8 @@ import { addQueryArgs } from '@wordpress/url'; import { useSelect, useDispatch } from '@wordpress/data'; import { useEvent } from '@wordpress/compose'; import { useView } from '@wordpress/views'; -import { Modal } from '@wordpress/components'; +// eslint-disable-next-line @wordpress/use-recommended-components +import { Dialog } from '@wordpress/ui'; import { store as noticesStore } from '@wordpress/notices'; /** @@ -54,6 +55,7 @@ export default function PageTemplates() { const [ selection, setSelection ] = useState( [ postId ] ); const [ selectedRegisteredTemplate, setSelectedRegisteredTemplate ] = useState( false ); + const [ isDuplicateOpen, setIsDuplicateOpen ] = useState( false ); const defaultView = DEFAULT_VIEW; const activeViewOverrides = useMemo( () => getActiveViewOverridesForTab( activeView ), @@ -342,6 +344,7 @@ export default function PageTemplates() { onClickItem={ ( item ) => { if ( typeof item.id === 'string' ) { setSelectedRegisteredTemplate( item ); + setIsDuplicateOpen( true ); } else { history.navigate( `/${ item.type }/${ item.id }?canvas=edit` @@ -359,17 +362,50 @@ export default function PageTemplates() { : false } /> - { selectedRegisteredTemplate && duplicateAction && ( - setSelectedRegisteredTemplate() } - size="small" + { duplicateAction && ( + { + if ( ! isOpen ) { + // Clear the cached template only after the exit + // animation finishes — keeps the modal contents + // rendered while the dialog slides out so the + // inner DataForm doesn't flash empty. The + // functional setter guards against a stale + // "complete" callback firing after the user has + // already re-opened the dialog with a different + // template (mid-animation re-open race). + setSelectedRegisteredTemplate( ( prev ) => + isDuplicateOpen ? prev : false + ); + } + } } > - setSelectedRegisteredTemplate() } - /> - + + + { __( 'Duplicate' ) } + + + + { selectedRegisteredTemplate && ( + // Keying on the template id ensures the + // RenderModal remounts with a fresh internal + // `useState` initializer if the user clicks a + // different row while the dialog is still + // closing — otherwise the previous template's + // title would persist into the new session. + + setIsDuplicateOpen( false ) + } + /> + ) } + + + ) } );