Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Site Editor: Add rename and trash actions to page panel #60232

Merged
merged 8 commits into from
Apr 5, 2024
20 changes: 15 additions & 5 deletions packages/edit-site/src/components/page-actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,30 @@ import { moreVertical } from '@wordpress/icons';
* Internal dependencies
*/
import TrashPageMenuItem from './trash-page-menu-item';
import RenamePostMenuItem from '../rename-post-menu-item';

export default function PageActions( { postId, toggleProps, onRemove } ) {
export default function PageActions( {
className,
onRemove,
page,
toggleProps,
} ) {
return (
<DropdownMenu
className={ className }
icon={ moreVertical }
label={ __( 'Actions' ) }
toggleProps={ toggleProps }
>
{ () => (
<MenuGroup>
<TrashPageMenuItem
postId={ postId }
onRemove={ onRemove }
/>
<RenamePostMenuItem post={ page } />
{ !! onRemove && (
<TrashPageMenuItem
page={ page }
onRemove={ onRemove }
/>
) }
</MenuGroup>
) }
</DropdownMenu>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,40 @@
/**
* WordPress dependencies
*/
import { useDispatch, useSelect } from '@wordpress/data';
import { useDispatch } from '@wordpress/data';
import { decodeEntities } from '@wordpress/html-entities';
import { store as coreStore } from '@wordpress/core-data';
import { __, sprintf } from '@wordpress/i18n';
import { MenuItem } from '@wordpress/components';
import { store as noticesStore } from '@wordpress/notices';

export default function TrashPageMenuItem( { postId, onRemove } ) {
export default function TrashPageMenuItem( { page, onRemove } ) {
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );
const { deleteEntityRecord } = useDispatch( coreStore );
const page = useSelect(
( select ) =>
select( coreStore ).getEntityRecord( 'postType', 'page', postId ),
[ postId ]

if ( page?.status === 'trash' ) {
return;
}

const title = decodeEntities(
typeof page.title === 'string' ? page.title : page.title.rendered
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is page.title not an object? I wish @wordpress/data handled this for us.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is page.title not an object?

When consuming an edited record, raw properties will be strings. It also depends on the REST API schema, but it should be the same for post types.

I wish @wordpress/data handled this for us.

It sort of does, but I always have a problem remembering which selector returns which data shape 😓

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question 👍

As George notes, this should now always be getting the edited record so the title value should be a string. I'll update this PR accordingly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was mistaken, it appears that the sidebar nav screen for pages is not using the edited record.

For the moment, this trash menu item is getting a record with a string value when accessed via the page panel but an object value in the site editor's sidebar nav. I'd like to explore consistently using the edited record in a follow-up.

);

async function removePage() {
try {
await deleteEntityRecord(
'postType',
'page',
postId,
page.id,
{},
{ throwOnError: true }
);
createSuccessNotice(
sprintf(
/* translators: The page's title. */
__( '"%s" moved to the Trash.' ),
decodeEntities( page.title.rendered )
title
),
{
type: 'snackbar',
Expand All @@ -50,14 +54,8 @@ export default function TrashPageMenuItem( { postId, onRemove } ) {
}
}
return (
<>
<MenuItem
onClick={ () => removePage() }
isDestructive
variant="secondary"
>
{ __( 'Move to Trash' ) }
</MenuItem>
</>
<MenuItem onClick={ () => removePage() } isDestructive>
{ __( 'Move to Trash' ) }
</MenuItem>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import { decodeEntities } from '@wordpress/html-entities';
*/
import { TEMPLATE_POST_TYPE } from '../../utils/constants';

export default function RenameMenuItem( { template, onClose } ) {
const title = decodeEntities( template.title.rendered );
export default function RenamePostMenuItem( { post } ) {
const title = decodeEntities(
typeof post.title === 'string' ? post.title : post.title.rendered
);
const [ editedTitle, setEditedTitle ] = useState( title );
const [ isModalOpen, setIsModalOpen ] = useState( false );

Expand All @@ -33,53 +35,46 @@ export default function RenameMenuItem( { template, onClose } ) {
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );

if ( template.type === TEMPLATE_POST_TYPE && ! template.is_custom ) {
if ( post.type === TEMPLATE_POST_TYPE && ! post.is_custom ) {
return null;
}

async function onTemplateRename( event ) {
async function onRename( event ) {
event.preventDefault();

if ( editedTitle === title ) {
return;
}

try {
await editEntityRecord( 'postType', template.type, template.id, {
await editEntityRecord( 'postType', post.type, post.id, {
title: editedTitle,
} );

// Update state before saving rerenders the list.
setEditedTitle( '' );
setIsModalOpen( false );
onClose();

// Persist edited entity.
await saveSpecifiedEntityEdits(
'postType',
template.type,
template.id,
post.type,
post.id,
[ 'title' ], // Only save title to avoid persisting other edits.
{
throwOnError: true,
}
);

createSuccessNotice(
template.type === TEMPLATE_POST_TYPE
? __( 'Template renamed.' )
: __( 'Template part renamed.' ),
{
type: 'snackbar',
}
);
createSuccessNotice( __( 'Name updated.' ), {
id: 'template-update',
type: 'snackbar',
} );
} catch ( error ) {
const fallbackErrorMessage =
template.type === TEMPLATE_POST_TYPE
? __( 'An error occurred while renaming the template.' )
: __(
'An error occurred while renaming the template part.'
);
const errorMessage =
error.message && error.code !== 'unknown_error'
? error.message
: fallbackErrorMessage;
: __( 'An error occurred while updating the name.' );

createErrorNotice( errorMessage, { type: 'snackbar' } );
}
Expand All @@ -102,8 +97,9 @@ export default function RenameMenuItem( { template, onClose } ) {
setIsModalOpen( false );
} }
overlayClassName="edit-site-list__rename-modal"
focusOnMount="firstContentElement"
>
<form onSubmit={ onTemplateRename }>
<form onSubmit={ onRename }>
<VStack spacing="5">
<TextControl
__nextHasNoMarginBottom
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,48 +15,62 @@ import {
store as editorStore,
privateApis as editorPrivateApis,
} from '@wordpress/editor';
import { privateApis as routerPrivateApis } from '@wordpress/router';

/**
* Internal dependencies
*/
import { store as editSiteStore } from '../../../store';
import PageActions from '../../page-actions';
import PageContent from './page-content';
import PageSummary from './page-summary';

import { unlock } from '../../../lock-unlock';

const { PostCardPanel } = unlock( editorPrivateApis );
const { useHistory } = unlock( routerPrivateApis );

export default function PagePanels() {
const { id, type, hasResolved, status, date, password, renderingMode } =
useSelect( ( select ) => {
const { getEditedPostContext } = select( editSiteStore );
const { getEditedEntityRecord, hasFinishedResolution } =
select( coreStore );
const { getRenderingMode } = select( editorStore );
const context = getEditedPostContext();
const queryArgs = [ 'postType', context.postType, context.postId ];
const page = getEditedEntityRecord( ...queryArgs );
return {
hasResolved: hasFinishedResolution(
'getEditedEntityRecord',
queryArgs
),
id: page?.id,
type: page?.type,
status: page?.status,
date: page?.date,
password: page?.password,
renderingMode: getRenderingMode(),
};
}, [] );
const { hasResolved, page, renderingMode } = useSelect( ( select ) => {
const { getEditedPostContext } = select( editSiteStore );
const { getEditedEntityRecord, hasFinishedResolution } =
select( coreStore );
const { getRenderingMode } = select( editorStore );
const context = getEditedPostContext();
const queryArgs = [ 'postType', context.postType, context.postId ];
return {
hasResolved: hasFinishedResolution(
'getEditedEntityRecord',
queryArgs
),
page: getEditedEntityRecord( ...queryArgs ),
renderingMode: getRenderingMode(),
};
}, [] );
const history = useHistory();

const { id, type, status, date, password } = page;

if ( ! hasResolved ) {
return null;
}

return (
<>
<PostCardPanel />
<PostCardPanel
actions={
<PageActions
page={ page }
className="edit-site-page-card__actions"
toggleProps={ { size: 'small' } }
onRemove={ () => {
history.push( {
path: '/page',
} );
} }
/>
}
/>
<PanelBody title={ __( 'Summary' ) }>
<PageSummary
status={ status }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

.edit-site-page-card__actions {
flex-shrink: 0;
}

.edit-site-page-panels__swap-template__confirm-modal__actions {
margin-top: $grid-unit-30;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export default function SidebarNavigationScreenPage( { backPath } ) {
actions={
<>
<PageActions
postId={ postId }
page={ record }
toggleProps={ { as: SidebarButton } }
onRemove={ () => {
goTo( '/page' );
Expand Down
6 changes: 3 additions & 3 deletions packages/edit-site/src/components/template-actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { decodeEntities } from '@wordpress/html-entities';
import { store as editSiteStore } from '../../store';
import isTemplateRemovable from '../../utils/is-template-removable';
import isTemplateRevertable from '../../utils/is-template-revertable';
import RenameMenuItem from './rename-menu-item';
import RenamePostMenuItem from '../rename-post-menu-item';
import { TEMPLATE_POST_TYPE } from '../../utils/constants';

export default function TemplateActions( {
Expand Down Expand Up @@ -55,8 +55,8 @@ export default function TemplateActions( {
<MenuGroup>
{ isRemovable && (
<>
<RenameMenuItem
template={ template }
<RenamePostMenuItem
post={ template }
onClose={ onClose }
/>
<DeleteMenuItem
Expand Down
Loading