Skip to content

Commit

Permalink
DataViews Extensibility: Allow unregistering the reorder-page action
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed Aug 7, 2024
1 parent 50419e5 commit a5b6f30
Show file tree
Hide file tree
Showing 7 changed files with 168 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
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

0 comments on commit a5b6f30

Please sign in to comment.