Skip to content

Commit 5c980de

Browse files
authored
Migrate Post Template Delete button from confirm() to ConfirmDialog (WordPress#37535)
* migrate post template menu item from `confirm()` to `ConfirmDialog` * migrate the `Delete template` menu item from `confirm()` to `ConfirmDialog` * simplify ConfirmDialog state using the built in isOpen prop * explicitly close ConfirmDialog after confirmation * add e2e tests for Delete Template ConfirmDialog * replace editor animation that was removed for local testing * implement new `deleteAllTemplates()` util * simplify xpath for increased resiliancy when switching to template edit mode * save and reload post to ensure template dropdown is properly updated * replace an unneeded reload with a more user-relatable button click * ensure small viewports are able to open/close settings panel correctly
1 parent c17bd22 commit 5c980de

File tree

2 files changed

+236
-48
lines changed

2 files changed

+236
-48
lines changed

packages/e2e-tests/specs/editor/various/post-editor-template-mode.test.js

Lines changed: 179 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
activatePlugin,
1212
deactivatePlugin,
1313
deleteAllTemplates,
14+
setBrowserViewport,
1415
} from '@wordpress/e2e-test-utils';
1516

1617
const openSidebarPanelWithTitle = async ( title ) => {
@@ -56,7 +57,7 @@ const switchToTemplateMode = async () => {
5657

5758
// Check that we switched properly to edit mode.
5859
await page.waitForXPath(
59-
'//*[contains(@class, "components-snackbar")]/*[text()="Editing template. Changes made here affect all posts and pages that use the template."]'
60+
'//*[text()="Editing template. Changes made here affect all posts and pages that use the template."]'
6061
);
6162
const title = await page.$eval(
6263
'.edit-post-template-top-area',
@@ -119,7 +120,6 @@ describe( 'Post Editor Template mode', () => {
119120
// there's a template resolution bug forcing us to do so.
120121
await saveDraft();
121122
await page.reload();
122-
123123
await switchToTemplateMode();
124124

125125
// Edit the template
@@ -197,3 +197,180 @@ describe( 'Post Editor Template mode', () => {
197197
expect( content ).toMatchSnapshot();
198198
} );
199199
} );
200+
201+
describe( 'Delete Post Template Confirmation Dialog', () => {
202+
beforeAll( async () => {
203+
await activatePlugin( 'gutenberg-test-block-templates' );
204+
await deleteAllTemplates( 'wp_template' );
205+
await deleteAllTemplates( 'wp_template_part' );
206+
await activateTheme( 'twentytwentyone' );
207+
await createNewPost();
208+
// Create a random post.
209+
await page.type( '.editor-post-title__input', 'Just an FSE Post' );
210+
await page.keyboard.press( 'Enter' );
211+
await page.keyboard.type( 'Hello World' );
212+
213+
// Save the post
214+
// Saving shouldn't be necessary but unfortunately,
215+
// there's a template resolution bug forcing us to do so.
216+
await saveDraft();
217+
await page.reload();
218+
// Unselect the blocks.
219+
await page.evaluate( () => {
220+
wp.data.dispatch( 'core/block-editor' ).clearSelectedBlock();
221+
} );
222+
} );
223+
224+
afterAll( async () => {
225+
await activateTheme( 'twentytwentyone' );
226+
await deactivatePlugin( 'gutenberg-test-block-templates' );
227+
} );
228+
229+
[ 'large', 'small' ].forEach( ( viewport ) => {
230+
it( `should retain template if deletion is canceled when the viewport is ${ viewport }`, async () => {
231+
await setBrowserViewport( viewport );
232+
233+
const isWelcomeGuideActive = await page.evaluate( () =>
234+
wp.data
235+
.select( 'core/edit-post' )
236+
.isFeatureActive( 'welcomeGuide' )
237+
);
238+
if ( isWelcomeGuideActive === true ) {
239+
await page.evaluate( () =>
240+
wp.data
241+
.dispatch( 'core/edit-post' )
242+
.toggleFeature( 'welcomeGuide' )
243+
);
244+
await page.reload();
245+
await page.waitForSelector( '.edit-post-layout' );
246+
}
247+
if ( viewport === 'small' ) {
248+
await page.waitForXPath( '//button[@aria-label="Settings"]' );
249+
await openDocumentSettingsSidebar();
250+
}
251+
const templateTitle = `${ viewport } Viewport Deletion Test`;
252+
253+
await createNewTemplate( templateTitle );
254+
// Edit the template
255+
if ( viewport === 'small' ) {
256+
await page.waitForXPath( `//h2[text()="${ templateTitle }"]` );
257+
const closeDocumentSettingsButton = await page.waitForXPath(
258+
'//button[@aria-label="Close settings"]'
259+
);
260+
await closeDocumentSettingsButton.click();
261+
}
262+
await insertBlock( 'Paragraph' );
263+
await page.keyboard.type(
264+
'Just a random paragraph added to the template'
265+
);
266+
267+
// Save changes
268+
const publishButton = await page.waitForXPath(
269+
`//button[contains(text(), 'Publish')]`
270+
);
271+
await publishButton.click();
272+
const saveButton = await page.waitForXPath(
273+
`//div[contains(@class, "entities-saved-states__panel-header")]/button[contains(text(), 'Save')]`
274+
);
275+
await saveButton.click();
276+
// Avoid publishing the post
277+
// Select the cancel button via parent div's class, because the text `Cancel` is used on another button as well
278+
const cancelButton = await page.waitForXPath(
279+
`//div[contains(@class,"editor-post-publish-panel__header-cancel-button")]/button[not(@disabled)]`
280+
);
281+
await cancelButton.click();
282+
283+
const templateDropdown = await page.waitForXPath(
284+
`//button[contains(text(), '${ templateTitle }')]`
285+
);
286+
await templateDropdown.click();
287+
const deleteTemplateButton = await page.waitForXPath(
288+
'//button[@role="menuitem"][@aria-label="Delete template"]'
289+
);
290+
await deleteTemplateButton.click();
291+
292+
await page.waitForXPath(
293+
`//*[text()="Are you sure you want to delete the ${ templateTitle } template? It may be used by other pages or posts."]`
294+
);
295+
const dialogCancelButton = await page.waitForXPath(
296+
'//*[@role="dialog"][not(@id="wp-link-wrap")]//button[text()="Cancel"]'
297+
);
298+
await dialogCancelButton.click();
299+
300+
const exitTemplateModeButton = await page.waitForXPath(
301+
'//button[text()="Back"]'
302+
);
303+
await exitTemplateModeButton.click();
304+
305+
await page.waitForXPath(
306+
'//button[@aria-label="Settings"][@aria-expanded="false"]'
307+
);
308+
await openDocumentSettingsSidebar();
309+
310+
const element = await page.waitForXPath(
311+
'//h2/button[contains(text(), "Template")]/../..//select'
312+
);
313+
const value = await element.getProperty( 'value' );
314+
const currentTemplateSlug = await value.jsonValue();
315+
316+
expect( currentTemplateSlug ).toBe(
317+
`wp-custom-template-${ viewport }-viewport-deletion-test`
318+
);
319+
} );
320+
321+
it( `should delete template if deletion is confirmed when the viewport is ${ viewport }`, async () => {
322+
const templateTitle = `${ viewport } Viewport Deletion Test`;
323+
324+
await setBrowserViewport( viewport );
325+
326+
await switchToTemplateMode();
327+
if ( viewport === 'small' ) {
328+
const closeDocumentSettingsButton = await page.waitForXPath(
329+
'//div[contains(@class,"interface-complementary-area-header__small")]/button[@aria-label="Close settings"]'
330+
);
331+
await closeDocumentSettingsButton.click();
332+
}
333+
334+
const templateDropdown = await page.waitForXPath(
335+
`//button[contains(text(), '${ templateTitle }')]`
336+
);
337+
await templateDropdown.click();
338+
339+
const deleteTemplateButton = await page.waitForXPath(
340+
'//button[@role="menuitem"][@aria-label="Delete template"]'
341+
);
342+
await deleteTemplateButton.click();
343+
344+
await page.waitForXPath(
345+
`//*[text()="Are you sure you want to delete the ${ templateTitle } template? It may be used by other pages or posts."]`
346+
);
347+
const dialogConfirmButton = await page.waitForXPath(
348+
'//*[@role="dialog"][not(@id="wp-link-wrap")]//button[text()="OK"]'
349+
);
350+
351+
await dialogConfirmButton.click();
352+
353+
// Saving isn't technically necessary, but for themes without any specified templates,
354+
// the removal of the Templates dropdown is delayed. A save and reload allows for this
355+
// delay and prevents flakiness
356+
await saveDraft();
357+
await page.reload();
358+
359+
const optionElementHandlers = await page.$x(
360+
'//h2/button[contains(text(), "Template")]/../..//select/option'
361+
);
362+
const availableTemplates = [];
363+
for ( const elem of optionElementHandlers ) {
364+
const elemName = await elem.getProperty( 'textContent' );
365+
const templateName = await elemName.jsonValue();
366+
availableTemplates.push( templateName );
367+
}
368+
369+
expect(
370+
availableTemplates.includes(
371+
`${ viewport } Viewport Deletion Test`
372+
)
373+
).toBe( false );
374+
} );
375+
} );
376+
} );

packages/edit-post/src/components/header/template-title/delete-template.js

Lines changed: 57 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@ import { pickBy } from 'lodash';
77
* WordPress dependencies
88
*/
99
import { __, sprintf } from '@wordpress/i18n';
10-
import { MenuGroup, MenuItem } from '@wordpress/components';
10+
import {
11+
MenuGroup,
12+
MenuItem,
13+
__experimentalConfirmDialog as ConfirmDialog,
14+
} from '@wordpress/components';
1115
import { store as blockEditorStore } from '@wordpress/block-editor';
1216
import { useDispatch, useSelect } from '@wordpress/data';
1317
import { store as editorStore } from '@wordpress/editor';
1418
import { store as coreStore } from '@wordpress/core-data';
19+
import { useState } from '@wordpress/element';
1520

1621
/**
1722
* Internal dependencies
@@ -33,6 +38,7 @@ export default function DeleteTemplate() {
3338
template: _isEditing ? getEditedPostTemplate() : null,
3439
};
3540
}, [] );
41+
const [ showConfirmDialog, setShowConfirmDialog ] = useState( false );
3642

3743
if ( ! template || ! template.wp_id ) {
3844
return null;
@@ -42,53 +48,58 @@ export default function DeleteTemplate() {
4248
templateTitle = template.title;
4349
}
4450

51+
const onDelete = () => {
52+
clearSelectedBlock();
53+
setIsEditingTemplate( false );
54+
setShowConfirmDialog( false );
55+
56+
editPost( {
57+
template: '',
58+
} );
59+
const settings = getEditorSettings();
60+
const newAvailableTemplates = pickBy(
61+
settings.availableTemplates,
62+
( _title, id ) => {
63+
return id !== template.slug;
64+
}
65+
);
66+
updateEditorSettings( {
67+
...settings,
68+
availableTemplates: newAvailableTemplates,
69+
} );
70+
deleteEntityRecord( 'postType', 'wp_template', template.id );
71+
};
72+
4573
return (
4674
<MenuGroup className="edit-post-template-top-area__second-menu-group">
47-
<MenuItem
48-
className="edit-post-template-top-area__delete-template-button"
49-
isDestructive
50-
variant="secondary"
51-
aria-label={ __( 'Delete template' ) }
52-
onClick={ () => {
53-
if (
54-
// eslint-disable-next-line no-alert
55-
window.confirm(
56-
sprintf(
57-
/* translators: %s: template name */
58-
__(
59-
'Are you sure you want to delete the %s template? It may be used by other pages or posts.'
60-
),
61-
templateTitle
62-
)
63-
)
64-
) {
65-
clearSelectedBlock();
66-
setIsEditingTemplate( false );
67-
68-
editPost( {
69-
template: '',
70-
} );
71-
const settings = getEditorSettings();
72-
const newAvailableTemplates = pickBy(
73-
settings.availableTemplates,
74-
( _title, id ) => {
75-
return id !== template.slug;
76-
}
77-
);
78-
updateEditorSettings( {
79-
...settings,
80-
availableTemplates: newAvailableTemplates,
81-
} );
82-
deleteEntityRecord(
83-
'postType',
84-
'wp_template',
85-
template.id
86-
);
87-
}
88-
} }
89-
>
90-
{ __( 'Delete template' ) }
91-
</MenuItem>
75+
<>
76+
<MenuItem
77+
className="edit-post-template-top-area__delete-template-button"
78+
isDestructive
79+
variant="secondary"
80+
aria-label={ __( 'Delete template' ) }
81+
onClick={ () => {
82+
setShowConfirmDialog( true );
83+
} }
84+
>
85+
{ __( 'Delete template' ) }
86+
</MenuItem>
87+
<ConfirmDialog
88+
isOpen={ showConfirmDialog }
89+
onConfirm={ onDelete }
90+
onCancel={ () => {
91+
setShowConfirmDialog( false );
92+
} }
93+
>
94+
{ sprintf(
95+
/* translators: %s: template name */
96+
__(
97+
'Are you sure you want to delete the %s template? It may be used by other pages or posts.'
98+
),
99+
templateTitle
100+
) }
101+
</ConfirmDialog>
102+
</>
92103
</MenuGroup>
93104
);
94105
}

0 commit comments

Comments
 (0)