Skip to content

Commit

Permalink
DataViews Extensibility: Allow unregistering the reorder-page action (#…
Browse files Browse the repository at this point in the history
…64199)

Co-authored-by: youknowriad <youknowriad@git.wordpress.org>
Co-authored-by: jsnajdr <jsnajdr@git.wordpress.org>
Co-authored-by: sirreal <jonsurrell@git.wordpress.org>
Co-authored-by: ntsekouras <ntsekouras@git.wordpress.org>
  • Loading branch information
5 people authored Aug 8, 2024
1 parent d578bb2 commit 5562199
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 120 deletions.
5 changes: 3 additions & 2 deletions packages/dataviews/src/normalize-fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Internal dependencies
*/
import getFieldTypeDefinition from './field-types';
import type { Field, NormalizedField } from './types';
import type { Field, NormalizedField, ItemRecord } from './types';

/**
* Apply default values and normalize the fields config.
Expand All @@ -18,7 +18,8 @@ export function normalizeFields< Item >(

const getValue =
field.getValue ||
( ( { item }: { item: Item } ) => item[ field.id as keyof Item ] );
// @ts-ignore
( ( { item }: { item: ItemRecord } ) => item[ field.id ] );

const sort =
field.sort ??
Expand Down
14 changes: 8 additions & 6 deletions packages/dataviews/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export type Operator =
| 'isAll'
| 'isNotAll';

export type ItemRecord = Record< string, unknown >;
export type ItemRecord = Object;

export type FieldType = 'text' | 'integer';

Expand Down Expand Up @@ -423,6 +423,12 @@ interface ActionBase< Item > {
supportsBulk?: boolean;
}

export interface RenderModalProps< Item > {
items: Item[];
closeModal?: () => void;
onActionPerformed?: ( items: Item[] ) => void;
}

export interface ActionModal< Item > extends ActionBase< Item > {
/**
* Modal to render when the action is triggered.
Expand All @@ -431,11 +437,7 @@ export interface ActionModal< Item > extends ActionBase< Item > {
items,
closeModal,
onActionPerformed,
}: {
items: Item[];
closeModal?: () => void;
onActionPerformed?: ( items: Item[] ) => void;
} ) => ReactElement;
}: RenderModalProps< Item > ) => ReactElement;

/**
* Whether to hide the modal header.
Expand Down
113 changes: 1 addition & 112 deletions packages/editor/src/components/post-actions/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { store as noticesStore } from '@wordpress/notices';
import { useMemo, useState, useEffect } from '@wordpress/element';
import { privateApis as patternsPrivateApis } from '@wordpress/patterns';
import { parse } from '@wordpress/blocks';
import { DataForm, isItemValid } from '@wordpress/dataviews';
import { DataForm } from '@wordpress/dataviews';
import {
Button,
TextControl,
Expand Down Expand Up @@ -58,10 +58,6 @@ const formDuplicateAction = {
fields: [ 'title' ],
};

const formOrderAction = {
fields: [ 'menu_order' ],
};

/**
* Check if a template is removable.
*
Expand Down Expand Up @@ -238,110 +234,6 @@ const renamePostAction = {
},
};

function ReorderModal( { items, closeModal, onActionPerformed } ) {
const [ item, setItem ] = useState( items[ 0 ] );
const orderInput = item.menu_order;
const { editEntityRecord, saveEditedEntityRecord } =
useDispatch( coreStore );
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );

async function onOrder( event ) {
event.preventDefault();

if ( ! isItemValid( item, fields, formOrderAction ) ) {
return;
}

try {
await editEntityRecord( 'postType', item.type, item.id, {
menu_order: orderInput,
} );
closeModal();
// Persist edited entity.
await saveEditedEntityRecord( 'postType', item.type, item.id, {
throwOnError: true,
} );
createSuccessNotice( __( 'Order updated' ), {
type: 'snackbar',
} );
onActionPerformed?.( items );
} catch ( error ) {
const errorMessage =
error.message && error.code !== 'unknown_error'
? error.message
: __( 'An error occurred while updating the order' );
createErrorNotice( errorMessage, {
type: 'snackbar',
} );
}
}
const isSaveDisabled = ! isItemValid( item, fields, formOrderAction );
return (
<form onSubmit={ onOrder }>
<VStack spacing="5">
<div>
{ __(
'Determines the order of pages. Pages with the same order value are sorted alphabetically. Negative order values are supported.'
) }
</div>
<DataForm
data={ item }
fields={ fields }
form={ formOrderAction }
onChange={ setItem }
/>
<HStack justify="right">
<Button
__next40pxDefaultSize
variant="tertiary"
onClick={ () => {
closeModal();
} }
>
{ __( 'Cancel' ) }
</Button>
<Button
__next40pxDefaultSize
variant="primary"
type="submit"
accessibleWhenDisabled
disabled={ isSaveDisabled }
__experimentalIsFocusable
>
{ __( 'Save' ) }
</Button>
</HStack>
</VStack>
</form>
);
}

function useReorderPagesAction( postType ) {
const supportsPageAttributes = useSelect(
( select ) => {
const { getPostType } = select( coreStore );
const postTypeObject = getPostType( postType );

return !! postTypeObject?.supports?.[ 'page-attributes' ];
},
[ postType ]
);

return useMemo(
() =>
supportsPageAttributes && {
id: 'order-pages',
label: __( 'Order' ),
isEligible( { status } ) {
return status !== 'trash';
},
RenderModal: ReorderModal,
},
[ supportsPageAttributes ]
);
}

const useDuplicatePostAction = ( postType ) => {
const userCanCreatePost = useSelect(
( select ) => {
Expand Down Expand Up @@ -595,7 +487,6 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
}, [ registerPostTypeActions, postType ] );

const duplicatePostAction = useDuplicatePostAction( postType );
const reorderPagesAction = useReorderPagesAction( postType );
const isTemplateOrTemplatePart = [
TEMPLATE_POST_TYPE,
TEMPLATE_PART_POST_TYPE,
Expand All @@ -622,7 +513,6 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
duplicateTemplatePartAction,
isPattern && userCanCreatePostType && duplicatePatternAction,
supportsTitle && renamePostAction,
reorderPagesAction,
...defaultActions,
].filter( Boolean );
// Filter actions based on provided context. If not provided
Expand Down Expand Up @@ -693,7 +583,6 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
isPattern,
postTypeObject?.viewable,
duplicatePostAction,
reorderPagesAction,
onActionPerformed,
isLoaded,
supportsRevisions,
Expand Down
3 changes: 3 additions & 0 deletions packages/editor/src/dataviews/actions/reorder-page.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const reorderPage = undefined;

export default reorderPage;
120 changes: 120 additions & 0 deletions packages/editor/src/dataviews/actions/reorder-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* WordPress dependencies
*/
import { useDispatch } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { __ } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';
import { useState } from '@wordpress/element';
import { DataForm, isItemValid } from '@wordpress/dataviews';
import {
Button,
__experimentalHStack as HStack,
__experimentalVStack as VStack,
} from '@wordpress/components';
import type { Action, RenderModalProps } from '@wordpress/dataviews';

/**
* Internal dependencies
*/
import type { CoreDataError, PostWithPageAttributesSupport } from '../types';
import { orderField } from '../fields';

const fields = [ orderField ];
const formOrderAction = {
fields: [ 'menu_order' ],
};

function ReorderModal( {
items,
closeModal,
onActionPerformed,
}: RenderModalProps< PostWithPageAttributesSupport > ) {
const [ item, setItem ] = useState( items[ 0 ] );
const orderInput = item.menu_order;
const { editEntityRecord, saveEditedEntityRecord } =
useDispatch( coreStore );
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );

async function onOrder( event: React.FormEvent ) {
event.preventDefault();

if ( ! isItemValid( item, fields, formOrderAction ) ) {
return;
}

try {
await editEntityRecord( 'postType', item.type, item.id, {
menu_order: orderInput,
} );
closeModal?.();
// Persist edited entity.
await saveEditedEntityRecord( 'postType', item.type, item.id, {
throwOnError: true,
} );
createSuccessNotice( __( 'Order updated.' ), {
type: 'snackbar',
} );
onActionPerformed?.( items );
} catch ( error ) {
const typedError = error as CoreDataError;
const errorMessage =
typedError.message && typedError.code !== 'unknown_error'
? typedError.message
: __( 'An error occurred while updating the order' );
createErrorNotice( errorMessage, {
type: 'snackbar',
} );
}
}
const isSaveDisabled = ! isItemValid( item, fields, formOrderAction );
return (
<form onSubmit={ onOrder }>
<VStack spacing="5">
<div>
{ __(
'Determines the order of pages. Pages with the same order value are sorted alphabetically. Negative order values are supported.'
) }
</div>
<DataForm
data={ item }
fields={ fields }
form={ formOrderAction }
onChange={ setItem }
/>
<HStack justify="right">
<Button
__next40pxDefaultSize
variant="tertiary"
onClick={ () => {
closeModal?.();
} }
>
{ __( 'Cancel' ) }
</Button>
<Button
__next40pxDefaultSize
variant="primary"
type="submit"
accessibleWhenDisabled
disabled={ isSaveDisabled }
>
{ __( 'Save' ) }
</Button>
</HStack>
</VStack>
</form>
);
}

const reorderPage: Action< PostWithPageAttributesSupport > = {
id: 'order-pages',
label: __( 'Order' ),
isEligible( { status } ) {
return status !== 'trash';
},
RenderModal: ReorderModal,
};

export default reorderPage;
25 changes: 25 additions & 0 deletions packages/editor/src/dataviews/fields/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import type { Field } from '@wordpress/dataviews';

/**
* Internal dependencies
*/
import type { BasePost, PostWithPageAttributesSupport } from '../types';

export const titleField: Field< BasePost > = {
type: 'text',
id: 'title',
label: __( 'Title' ),
placeholder: __( 'No title' ),
getValue: ( { item } ) => item.title,
};

export const orderField: Field< PostWithPageAttributesSupport > = {
type: 'integer',
id: 'menu_order',
label: __( 'Order' ),
description: __( 'Determines the order of pages.' ),
};
4 changes: 4 additions & 0 deletions packages/editor/src/dataviews/store/private-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import resetPost from '../actions/reset-post';
import trashPost from '../actions/trash-post';
import permanentlyDeletePost from '../actions/permanently-delete-post';
import restorePost from '../actions/restore-post';
import reorderPage from '../actions/reorder-page';
import type { PostType } from '../types';
import { store as editorStore } from '../../store';
import { unlock } from '../../lock-unlock';
Expand Down Expand Up @@ -73,6 +74,9 @@ export const registerPostTypeActions =
.getPostType( postType ) ) as PostType;

const actions = [
postTypeConfig?.supports?.[ 'page-attributes' ]
? reorderPage
: undefined,
postTypeConfig.slug === 'wp_block' ? exportPattern : undefined,
resetPost,
restorePost,
Expand Down
7 changes: 7 additions & 0 deletions packages/editor/src/dataviews/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export interface Pattern extends BasePost {
wp_pattern_sync_status: string;
}

export interface PostWithPageAttributesSupport extends BasePost {
menu_order: number;
}

export type Post = TemplateOrTemplatePart | Pattern | BasePost;

export type PostWithPermissions = Post & {
Expand All @@ -38,6 +42,9 @@ export type PostWithPermissions = Post & {

export interface PostType {
slug: string;
supports?: {
'page-attributes'?: boolean;
};
}

// Will be unnecessary after typescript 5.0 upgrade.
Expand Down

1 comment on commit 5562199

@github-actions
Copy link

Choose a reason for hiding this comment

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

Flaky tests detected in 5562199.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/10299441832
📝 Reported issues:

Please sign in to comment.