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

[core-data] Document and add types for dynamic actions and selectors. #67668

Open
wants to merge 17 commits into
base: trunk
Choose a base branch
from
Open
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
106 changes: 106 additions & 0 deletions packages/core-data/src/dynamic-entities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* Internal dependencies
*/
import type { GetRecordsHttpQuery, State } from './selectors';
import type * as ET from './entity-types';

export type WPEntityTypes< C extends ET.Context = 'edit' > = {
Comment: ET.Comment< C >;
Media: ET.Attachment< C >;
Menu: ET.NavMenu< C >;
MenuItem: ET.NavMenuItem< C >;
MenuLocation: ET.MenuLocation< C >;
Plugin: ET.Plugin< C >;
PostType: ET.Type< C >;
Revision: ET.PostRevision< C >;
Sidebar: ET.Sidebar< C >;
Site: ET.Settings< C >;
Status: ET.PostStatusObject< C >;
Taxonomy: ET.Taxonomy< C >;
Theme: ET.Theme< C >;
UnstableBase: ET.UnstableBase< C >;
User: ET.User< C >;
Widget: ET.Widget< C >;
WidgetType: ET.WidgetType< C >;
};

/**
* A simple utility that pluralizes a string.
* Converts:
* - "post" to "posts"
* - "taxonomy" to "taxonomies"
* - "media" to "mediaItems"
* - "status" to "statuses"
*/
type PluralizeEntity< T extends string > = T extends 'Media'
? 'MediaItems'
: T extends 'Status'
? 'Statuses'
: T extends `${ infer U }y`
? `${ U }ies`
: `${ T }s`;

/**
* A simple utility that singularizes a string.
*
* Converts:
* - "posts" to "post"
* - "taxonomies" to "taxonomy"
* - "mediaItems" to "media"
* - "statuses" to "status"
*/
type SingularizeEntity< T extends string > = T extends 'MediaItems'
? 'Media'
: T extends 'Statuses'
? 'Status'
: T extends `${ infer U }ies`
? `${ U }y`
: T extends `${ infer U }s`
? U
: T;

export type SingularGetters = {
[ Key in `get${ keyof WPEntityTypes }` ]: (
state: State,
id: number | string,
query?: GetRecordsHttpQuery
) => WPEntityTypes[ Key extends `get${ infer E }` ? E : never ] | undefined;
};

export type PluralGetters = {
[ Key in `get${ PluralizeEntity< keyof WPEntityTypes > }` ]: (
state: State,
query?: GetRecordsHttpQuery
) => Array<
WPEntityTypes[ Key extends `get${ infer E }`
? SingularizeEntity< E >
: never ]
> | null;
};

type ActionOptions = {
throwOnError?: boolean;
};

type DeleteRecordsHttpQuery = Record< string, any >;

export type SaveActions = {
[ Key in `save${ keyof WPEntityTypes }` ]: (
data: Partial<
WPEntityTypes[ Key extends `save${ infer E }` ? E : never ]
>,
options?: ActionOptions
) => Promise< void >;
};

export type DeleteActions = {
[ Key in `delete${ keyof WPEntityTypes }` ]: (
id: number | string,
query?: DeleteRecordsHttpQuery,
options?: ActionOptions
) => Promise< void >;
};

export let dynamicActions: SaveActions & DeleteActions;

export let dynamicSelectors: SingularGetters & PluralGetters;
84 changes: 84 additions & 0 deletions packages/core-data/src/entity-types/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* Internal dependencies
*/
import type { Context, OmitNevers } from './helpers';
import type { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records';

type TemplatePartArea = {
area: string;
label: string;
icon: string;
description: string;
};

type TemplateType = {
title: string;
description: string;
slug: string;
};

declare module './base-entity-records' {
export namespace BaseEntityRecords {
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
export interface Base< C extends Context > {
/**
* Site description.
*/
description: string;

/**
* GMT offset for the site.
*/
gmt_offset: string | number;

/**
* Home URL.
*/
home: string;

/**
* Site title
*/
name: string;

/**
* Site icon ID.
*/
site_icon?: number;

/**
* Site icon URL.
*/
site_icon_url: string;

/**
* Site logo ID.
*/
site_logo?: number;

/**
* Site timezone string.
*/
timezone_string: string;

/**
* Site URL.
*/
url: string;

/**
* Default template part areas.
*/
default_template_part_areas?: Array< TemplatePartArea >;

/**
* Default template types
*/
default_template_types?: Array< TemplateType >;
}
}
}

export type Base< C extends Context = 'edit' > = OmitNevers<
_BaseEntityRecords.Base< C >
>;
6 changes: 6 additions & 0 deletions packages/core-data/src/entity-types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import type { NavMenuItem } from './nav-menu-item';
import type { Page } from './page';
import type { Plugin } from './plugin';
import type { Post } from './post';
import type { PostStatusObject } from './post-status';
import type { Base } from './base';
import type { PostRevision } from './post-revision';
import type { Settings } from './settings';
import type { Sidebar } from './sidebar';
Expand All @@ -27,6 +29,7 @@ export type { BaseEntityRecords } from './base-entity-records';

export type {
Attachment,
Base as UnstableBase,
Comment,
Context,
GlobalStylesRevision,
Expand All @@ -36,6 +39,7 @@ export type {
Page,
Plugin,
Post,
PostStatusObject,
PostRevision,
Settings,
Sidebar,
Expand Down Expand Up @@ -84,6 +88,7 @@ export type {
*/
export interface PerPackageEntityRecords< C extends Context > {
core:
| Base< C >
| Attachment< C >
| Comment< C >
| GlobalStylesRevision< C >
Expand All @@ -93,6 +98,7 @@ export interface PerPackageEntityRecords< C extends Context > {
| Page< C >
| Plugin< C >
| Post< C >
| PostStatusObject< C >
| PostRevision< C >
| Settings< C >
| Sidebar< C >
Expand Down
56 changes: 56 additions & 0 deletions packages/core-data/src/entity-types/post-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Internal dependencies
*/
import type { Context, OmitNevers } from './helpers';
import type { BaseEntityRecords as _BaseEntityRecords } from './base-entity-records';

declare module './base-entity-records' {
export namespace BaseEntityRecords {
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
export interface PostStatusObject< C extends Context > {
/**
* The title for the status.
*/
name: string;

/**
* Whether posts with this status should be private.
*/
private: boolean;

/**
* Whether posts with this status should be protected.
*/
protected: boolean;

/**
* Whether posts of this status should be shown in the front end of the site.
*/
public: boolean;

/**
* Whether posts with this status should be publicly-queryable.
*/
queryable: boolean;

/**
* Whether to include posts in the edit listing for their post type.
*/
show_in_list: boolean;

/**
* An alphanumeric identifier for the status.
*/
slug: string;

/**
* Whether posts of this status may have floating published dates.
*/
date_floating: boolean;
}
}
}

export type PostStatusObject< C extends Context = 'edit' > = OmitNevers<
_BaseEntityRecords.Type< C >
>;
14 changes: 12 additions & 2 deletions packages/core-data/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from './entities';
import { STORE_NAME } from './name';
import { unlock } from './lock-unlock';
import { dynamicActions, dynamicSelectors } from './dynamic-entities';

// The entity selectors/resolvers and actions are shortcuts to their generic equivalents
// (getEntityRecord, getEntityRecords, updateEntityRecord, updateEntityRecords)
Expand Down Expand Up @@ -68,8 +69,17 @@ const entityActions = entitiesConfig.reduce( ( result, entity ) => {

const storeConfig = () => ( {
reducer,
actions: { ...actions, ...entityActions, ...createLocksActions() },
selectors: { ...selectors, ...entitySelectors },
actions: {
...dynamicActions,
...actions,
...entityActions,
...createLocksActions(),
},
selectors: {
...dynamicSelectors,
...selectors,
...entitySelectors,
},
resolvers: { ...resolvers, ...entityResolvers },
} );

Expand Down
2 changes: 1 addition & 1 deletion packages/core-data/src/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ type Optional< T > = T | undefined;
/**
* HTTP Query parameters sent with the API request to fetch the entity records.
*/
type GetRecordsHttpQuery = Record< string, any >;
export type GetRecordsHttpQuery = Record< string, any >;

/**
* Arguments for EntityRecord selectors.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
__experimentalVStack as VStack,
} from '@wordpress/components';
import { useInstanceId } from '@wordpress/compose';
import type { UnstableBase } from '@wordpress/core-data';
import { store as coreStore } from '@wordpress/core-data';
import { useDispatch, useSelect } from '@wordpress/data';
import { useState } from '@wordpress/element';
Expand Down Expand Up @@ -52,13 +53,6 @@ type CreateTemplatePartModalContentsProps = {
defaultTitle?: string;
};

type TemplatePartArea = {
area: string;
label: string;
icon: string;
description: string;
};

/**
* A React component that renders a modal for creating a template part. The modal displays a title and the contents for creating the template part.
* This component should not live in this package, it should be moved to a dedicated package responsible for managing template.
Expand All @@ -73,7 +67,6 @@ export default function CreateTemplatePartModal( {
} & CreateTemplatePartModalContentsProps ) {
const defaultModalTitle = useSelect(
( select ) =>
// @ts-expect-error getPostType is not typed with 'wp_template_part' as argument.
select( coreStore ).getPostType( 'wp_template_part' )?.labels
?.add_new_item,
[]
Expand Down Expand Up @@ -135,9 +128,8 @@ export function CreateTemplatePartModalContents( {

const defaultTemplatePartAreas = useSelect(
( select ) =>
// @ts-expect-error getEntityRecord is not typed with unstableBase as argument.
select( coreStore ).getEntityRecord< {
default_template_part_areas: Array< TemplatePartArea >;
default_template_part_areas: UnstableBase[ 'default_template_part_areas' ];
} >( 'root', '__unstableBase' )?.default_template_part_areas,
[]
);
Expand Down
2 changes: 0 additions & 2 deletions packages/fields/src/fields/parent/parent-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ export function PageAttributesParent( {

const { parentPostTitle, pageItems, isHierarchical } = useSelect(
( select ) => {
// @ts-expect-error getPostType is not typed
const { getEntityRecord, getEntityRecords, getPostType } =
select( coreStore );

Expand Down Expand Up @@ -289,7 +288,6 @@ export const ParentEdit = ( {
const { id } = field;

const homeUrl = useSelect( ( select ) => {
// @ts-expect-error getEntityRecord is not typed with unstableBase as argument.
return select( coreStore ).getEntityRecord< {
home: string;
} >( 'root', '__unstableBase' )?.home as string;
Expand Down
Loading