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

DataViews: Bootstrap Actions Extensibility API #62052

Merged
merged 2 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/dataviews/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as DataViews } from './dataviews';
export { VIEW_LAYOUTS } from './layouts';
export { filterSortAndPaginate } from './filter-and-sort-data-view';
export type * from './types';
24 changes: 24 additions & 0 deletions packages/editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1504,6 +1504,18 @@ _Returns_

Undocumented declaration.

### registerEntityAction

Registers a new DataViews action.

This is an experimental API and is subject to change. it's only available in the Gutenberg plugin for now.

_Parameters_

- _kind_ `string`: Entity kind.
- _name_ `string`: Entity name.
- _config_ `Action`: Action configuration.

### RichText

> **Deprecated** since 5.3, use `wp.blockEditor.RichText` instead.
Expand Down Expand Up @@ -1589,6 +1601,18 @@ Undocumented declaration.

Undocumented declaration.

### unregisterEntityAction

Unregisters a DataViews action.

This is an experimental API and is subject to change. it's only available in the Gutenberg plugin for now.

_Parameters_

- _kind_ `string`: Entity kind.
- _name_ `string`: Entity name.
- _actionId_ `string`: Action ID.

### UnsavedChangesWarning

Warns the user if there are unsaved changes before leaving the editor. Compatible with Post Editor and Site Editor.
Expand Down
6 changes: 5 additions & 1 deletion packages/editor/src/components/post-actions/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1030,11 +1030,13 @@ export const duplicateTemplatePartAction = {
};

export function usePostActions( postType, onActionPerformed ) {
const { postTypeObject } = useSelect(
const { defaultActions, postTypeObject } = useSelect(
( select ) => {
const { getPostType } = select( coreStore );
const { getEntityActions } = unlock( select( editorStore ) );
return {
postTypeObject: getPostType( postType ),
defaultActions: getEntityActions( 'postType', postType ),
};
},
[ postType ]
Expand Down Expand Up @@ -1072,6 +1074,7 @@ export function usePostActions( postType, onActionPerformed ) {
? deletePostAction
: trashPostAction,
! isTemplateOrTemplatePart && permanentlyDeletePostAction,
...defaultActions,
].filter( Boolean );

if ( onActionPerformed ) {
Expand Down Expand Up @@ -1117,6 +1120,7 @@ export function usePostActions( postType, onActionPerformed ) {

return actions;
}, [
defaultActions,
isTemplateOrTemplatePart,
isPattern,
postTypeObject?.viewable,
Expand Down
55 changes: 55 additions & 0 deletions packages/editor/src/dataviews/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* WordPress dependencies
*/
import { dispatch } from '@wordpress/data';

/**
* Internal dependencies
*/
import { unlock } from '../lock-unlock';
import { store as editorStore } from '../store';

/**
* @typedef {import('@wordpress/dataviews').Action} Action
*/

/**
* Registers a new DataViews action.
*
* This is an experimental API and is subject to change.
* it's only available in the Gutenberg plugin for now.
*
* @param {string} kind Entity kind.
* @param {string} name Entity name.
* @param {Action} config Action configuration.
*/

export function registerEntityAction( kind, name, config ) {
const { registerEntityAction: _registerEntityAction } = unlock(
dispatch( editorStore )
);

if ( globalThis.IS_GUTENBERG_PLUGIN ) {
_registerEntityAction( kind, name, config );
}
}

/**
* Unregisters a DataViews action.
*
* This is an experimental API and is subject to change.
* it's only available in the Gutenberg plugin for now.
*
* @param {string} kind Entity kind.
* @param {string} name Entity name.
* @param {string} actionId Action ID.
*/
export function unregisterEntityAction( kind, name, actionId ) {
const { unregisterEntityAction: _unregisterEntityAction } = unlock(
dispatch( editorStore )
);

if ( globalThis.IS_GUTENBERG_PLUGIN ) {
_unregisterEntityAction( kind, name, actionId );
}
}
30 changes: 30 additions & 0 deletions packages/editor/src/dataviews/store/private-actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* WordPress dependencies
*/
import type { Action, AnyItem } from '@wordpress/dataviews';

export function registerEntityAction< Item extends AnyItem >(
kind: string,
name: string,
config: Action< Item >
) {
return {
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this is the bootstrap PR, but we should add validation, even if it's in a follow up.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's one of the reasons I added the types, for us at least, everything is typed, so there's less need for validation but indeed, validation for third-parties would be great. I wonder if there's a runtime lib that can use typescript types to generate validation automatically.

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 added a todo list for this to the issue.

type: 'REGISTER_ENTITY_ACTION' as const,
kind,
name,
config,
};
}

export function unregisterEntityAction(
kind: string,
name: string,
actionId: string
) {
return {
type: 'UNREGISTER_ENTITY_ACTION' as const,
kind,
name,
actionId,
};
}
8 changes: 8 additions & 0 deletions packages/editor/src/dataviews/store/private-selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Internal dependencies
*/
import type { State } from './reducer';

export function getEntityActions( state: State, kind: string, name: string ) {
return state.actions[ kind ]?.[ name ] ?? [];
ntsekouras marked this conversation as resolved.
Show resolved Hide resolved
}
44 changes: 44 additions & 0 deletions packages/editor/src/dataviews/store/reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* WordPress dependencies
*/
import { combineReducers } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import type { Action } from '@wordpress/dataviews';

type ReduxAction =
| ReturnType< typeof import('./private-actions').registerEntityAction >
| ReturnType< typeof import('./private-actions').unregisterEntityAction >;

export type ActionState = Record< string, Record< string, Action< any >[] > >;
export type State = {
actions: ActionState;
};

function actions( state: ActionState = {}, action: ReduxAction ) {
switch ( action.type ) {
case 'REGISTER_ENTITY_ACTION':
return {
...state,
[ action.kind ]: {
[ action.name ]: [
...( state[ action.kind ]?.[ action.name ] ?? [] ),
action.config,
],
},
};
case 'UNREGISTER_ENTITY_ACTION': {
return {
...state,
[ action.kind ]: (
state[ action.kind ]?.[ action.name ] ?? []
).filter( ( _action ) => _action.id !== action.actionId ),
};
}
}

return state;
}

export default combineReducers( {
actions,
} );
1 change: 1 addition & 0 deletions packages/editor/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { storeConfig, store } from './store';
export * from './components';
export * from './utils';
export * from './private-apis';
export * from './dataviews/api';

/*
* Backward compatibility
Expand Down
1 change: 1 addition & 0 deletions packages/editor/src/store/private-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { decodeEntities } from '@wordpress/html-entities';
* Internal dependencies
*/
import isTemplateRevertable from './utils/is-template-revertable';
export * from '../dataviews/store/private-actions';

/**
* Returns an action object used to set which template is currently being used/edited.
Expand Down
5 changes: 5 additions & 0 deletions packages/editor/src/store/private-selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
} from './selectors';
import { TEMPLATE_PART_POST_TYPE } from './constants';
import { getFilteredTemplatePartBlocks } from './utils/get-filtered-template-parts';
import { getEntityActions as _getEntityActions } from '../dataviews/store/private-selectors';

const EMPTY_INSERTION_POINT = {
rootClientId: undefined,
Expand Down Expand Up @@ -180,3 +181,7 @@ export const hasPostMetaChanges = createRegistrySelector(
);
}
);

export function getEntityActions( state, ...args ) {
return _getEntityActions( state.dataviews, ...args );
}
2 changes: 2 additions & 0 deletions packages/editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { combineReducers } from '@wordpress/data';
* Internal dependencies
*/
import { EDITOR_SETTINGS_DEFAULTS } from './defaults';
import dataviewsReducer from '../dataviews/store/reducer';

/**
* Returns a post attribute value, flattening nested rendered content using its
Expand Down Expand Up @@ -402,4 +403,5 @@ export default combineReducers( {
listViewPanel,
listViewToggleRef,
publishSidebarActive,
dataviews: dataviewsReducer,
Copy link
Contributor

Choose a reason for hiding this comment

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

This is named like that because the plan is to have a separate package eventually? Because the actions are used beyond the Data Views, so I don't really get why name it like that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, I can rename, what would you suggest? I guess you can think of it as the package name.

} );
36 changes: 36 additions & 0 deletions packages/editor/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"$schema": "https://json.schemastore.org/tsconfig.json",
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"declarationDir": "build-types"
},
"references": [
{ "path": "../a11y" },
{ "path": "../api-fetch" },
{ "path": "../blob" },
{ "path": "../block-editor" },
{ "path": "../components" },
{ "path": "../compose" },
{ "path": "../core-data" },
{ "path": "../data" },
{ "path": "../dataviews" },
{ "path": "../date" },
{ "path": "../deprecated" },
{ "path": "../dom" },
{ "path": "../element" },
{ "path": "../hooks" },
{ "path": "../html-entities" },
{ "path": "../i18n" },
{ "path": "../icons" },
{ "path": "../keycodes" },
{ "path": "../notices" },
{ "path": "../plugins" },
{ "path": "../private-apis" },
{ "path": "../rich-text" },
{ "path": "../url" },
{ "path": "../warning" },
{ "path": "../wordcount" }
],
"include": [ "src/**/*.ts", "src/**/*.tsx" ]
}
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
{ "path": "packages/dom" },
{ "path": "packages/dom-ready" },
{ "path": "packages/e2e-test-utils-playwright" },
{ "path": "packages/editor" },
{ "path": "packages/element" },
{ "path": "packages/escape-html" },
{ "path": "packages/eslint-plugin" },
Expand Down
Loading