From 5c071500d8df8b0d17a7443d026a8c21d116b7da Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Wed, 4 Dec 2024 14:20:12 +0530 Subject: [PATCH 1/6] TypeScript: Convert notices package to TypeScript --- .../data/data-core-notices.md | 61 +++---- packages/notices/src/{index.js => index.ts} | 0 .../src/store/{actions.js => actions.ts} | 118 ++++++------- .../src/store/{constants.js => constants.ts} | 11 +- .../src/store/{controls.js => controls.ts} | 2 +- .../notices/src/store/{index.js => index.ts} | 2 + .../src/store/{reducer.js => reducer.ts} | 17 +- packages/notices/src/store/selectors.js | 73 -------- packages/notices/src/store/selectors.ts | 49 ++++++ packages/notices/src/store/types.ts | 160 ++++++++++++++++++ .../utils/{on-sub-key.js => on-sub-key.ts} | 17 +- packages/notices/tsconfig.json | 2 +- 12 files changed, 322 insertions(+), 190 deletions(-) rename packages/notices/src/{index.js => index.ts} (100%) rename packages/notices/src/store/{actions.js => actions.ts} (66%) rename packages/notices/src/store/{constants.js => constants.ts} (71%) rename packages/notices/src/store/{controls.js => controls.ts} (67%) rename packages/notices/src/store/{index.js => index.ts} (95%) rename packages/notices/src/store/{reducer.js => reducer.ts} (72%) delete mode 100644 packages/notices/src/store/selectors.js create mode 100644 packages/notices/src/store/selectors.ts create mode 100644 packages/notices/src/store/types.ts rename packages/notices/src/store/utils/{on-sub-key.js => on-sub-key.ts} (67%) diff --git a/docs/reference-guides/data/data-core-notices.md b/docs/reference-guides/data/data-core-notices.md index d36098429811d..7bdf15b3dde53 100644 --- a/docs/reference-guides/data/data-core-notices.md +++ b/docs/reference-guides/data/data-core-notices.md @@ -4,7 +4,7 @@ Namespace: `core/notices`. ## Selectors - + ### getNotices @@ -32,18 +32,18 @@ const ExampleComponent = () => { _Parameters_ -- _state_ `Object`: Notices state. -- _context_ `?string`: Optional grouping context. +- _state_ `Record< string, Array< Notice > >`: Notices state. +- _context_ Optional grouping context. _Returns_ -- `WPNotice[]`: Array of notices. +- Array of notices. - + ## Actions - + ### createErrorNotice @@ -83,11 +83,11 @@ const ExampleComponent = () => { _Parameters_ - _content_ `string`: Notice message. -- _options_ `[Object]`: Optional notice options. +- _options_ `[NoticeOptions]`: Optional notice options. _Returns_ -- `Object`: Action object. +- Action object. ### createInfoNotice @@ -124,11 +124,11 @@ const ExampleComponent = () => { _Parameters_ - _content_ `string`: Notice message. -- _options_ `[Object]`: Optional notice options. +- _options_ `[NoticeOptions]`: Optional notice options. _Returns_ -- `Object`: Action object. +- Action object. ### createNotice @@ -156,22 +156,13 @@ const ExampleComponent = () => { _Parameters_ -- _status_ `string|undefined`: Notice status ("info" if undefined is passed). +- _status_ Notice status ("info" if undefined is passed). - _content_ `string`: Notice message. -- _options_ `[Object]`: Notice options. -- _options.context_ `[string]`: Context under which to group notice. -- _options.id_ `[string]`: Identifier for notice. Automatically assigned if not specified. -- _options.isDismissible_ `[boolean]`: Whether the notice can be dismissed by user. -- _options.type_ `[string]`: Type of notice, one of `default`, or `snackbar`. -- _options.speak_ `[boolean]`: Whether the notice content should be announced to screen readers. -- _options.actions_ `[Array]`: User actions to be presented with notice. -- _options.icon_ `[string]`: An icon displayed with the notice. Only used when type is set to `snackbar`. -- _options.explicitDismiss_ `[boolean]`: Whether the notice includes an explicit dismiss button and can't be dismissed by clicking the body of the notice. Only applies when type is set to `snackbar`. -- _options.onDismiss_ `[Function]`: Called when the notice is dismissed. +- _options_ `[NoticeOptions]`: Notice options. _Returns_ -- `Object`: Action object. +- `Extract< ReducerAction, { type: 'CREATE_NOTICE'; } >`: Action object. ### createSuccessNotice @@ -209,11 +200,11 @@ const ExampleComponent = () => { _Parameters_ - _content_ `string`: Notice message. -- _options_ `[Object]`: Optional notice options. +- _options_ `[NoticeOptions]`: Optional notice options. _Returns_ -- `Object`: Action object. +- Action object. ### createWarningNotice @@ -255,11 +246,11 @@ const ExampleComponent = () => { _Parameters_ - _content_ `string`: Notice message. -- _options_ `[Object]`: Optional notice options. +- _options_ `[NoticeOptions]`: Optional notice options. _Returns_ -- `Object`: Action object. +- Action object. ### removeAllNotices @@ -301,12 +292,12 @@ export const ExampleComponent = () => { _Parameters_ -- _noticeType_ `string`: The context to remove all notices from. -- _context_ `string`: The context to remove all notices from. +- _noticeType_ The context to remove all notices from. +- _context_ The context to remove all notices from. _Returns_ -- `Object`: Action object. +- `Extract< ReducerAction, { type: 'REMOVE_ALL_NOTICES'; } >`: Action object. ### removeNotice @@ -350,11 +341,11 @@ const ExampleComponent = () => { _Parameters_ - _id_ `string`: Notice unique identifier. -- _context_ `[string]`: Optional context (grouping) in which the notice is intended to appear. Defaults to default context. +- _context_ Optional context (grouping) in which the notice is intended to appear. Defaults to 'default' context. _Returns_ -- `Object`: Action object. +- `Extract< ReducerAction, { type: 'REMOVE_NOTICE'; } >`: Action object. ### removeNotices @@ -394,11 +385,11 @@ const ExampleComponent = () => { _Parameters_ -- _ids_ `string[]`: List of unique notice identifiers. -- _context_ `[string]`: Optional context (grouping) in which the notices are intended to appear. Defaults to default context. +- _ids_ `Array< string >`: List of unique notice identifiers. +- _context_ Optional context (grouping) in which the notices are intended to appear. Defaults to 'default' context. _Returns_ -- `Object`: Action object. +- `Extract< ReducerAction, { type: 'REMOVE_NOTICES'; } >`: Action object. - + diff --git a/packages/notices/src/index.js b/packages/notices/src/index.ts similarity index 100% rename from packages/notices/src/index.js rename to packages/notices/src/index.ts diff --git a/packages/notices/src/store/actions.js b/packages/notices/src/store/actions.ts similarity index 66% rename from packages/notices/src/store/actions.js rename to packages/notices/src/store/actions.ts index fb1c424307351..46ca982e71394 100644 --- a/packages/notices/src/store/actions.js +++ b/packages/notices/src/store/actions.ts @@ -2,15 +2,12 @@ * Internal dependencies */ import { DEFAULT_CONTEXT, DEFAULT_STATUS } from './constants'; +import type { NoticeOptions, ReducerAction } from './types'; /** - * @typedef {Object} WPNoticeAction Object describing a user action option associated with a notice. + * @typedef {import('./types').NoticeAction} NoticeAction Notice action object. * - * @property {string} label Message to use as action label. - * @property {?string} url Optional URL of resource if action incurs - * browser navigation. - * @property {?Function} onClick Optional function to invoke when action is - * triggered by user. + * @typedef {import('./types').NoticeOptions} NoticeOptions Notice action object. */ let uniqueId = 0; @@ -18,32 +15,9 @@ let uniqueId = 0; /** * Returns an action object used in signalling that a notice is to be created. * - * @param {string|undefined} status Notice status ("info" if undefined is passed). - * @param {string} content Notice message. - * @param {Object} [options] Notice options. - * @param {string} [options.context='global'] Context under which to - * group notice. - * @param {string} [options.id] Identifier for notice. - * Automatically assigned - * if not specified. - * @param {boolean} [options.isDismissible=true] Whether the notice can - * be dismissed by user. - * @param {string} [options.type='default'] Type of notice, one of - * `default`, or `snackbar`. - * @param {boolean} [options.speak=true] Whether the notice - * content should be - * announced to screen - * readers. - * @param {Array} [options.actions] User actions to be - * presented with notice. - * @param {string} [options.icon] An icon displayed with the notice. - * Only used when type is set to `snackbar`. - * @param {boolean} [options.explicitDismiss] Whether the notice includes - * an explicit dismiss button and - * can't be dismissed by clicking - * the body of the notice. Only applies - * when type is set to `snackbar`. - * @param {Function} [options.onDismiss] Called when the notice is dismissed. + * @param status Notice status ("info" if undefined is passed). + * @param content Notice message. + * @param [options] Notice options. * * @example * ```js @@ -64,9 +38,13 @@ let uniqueId = 0; * }; * ``` * - * @return {Object} Action object. + * @return Action object. */ -export function createNotice( status = DEFAULT_STATUS, content, options = {} ) { +export function createNotice( + status = DEFAULT_STATUS, + content: string, + options: NoticeOptions = {} +): Extract< ReducerAction, { type: 'CREATE_NOTICE' } > { const { speak = true, isDismissible = true, @@ -110,8 +88,8 @@ export function createNotice( status = DEFAULT_STATUS, content, options = {} ) { * * @see createNotice * - * @param {string} content Notice message. - * @param {Object} [options] Optional notice options. + * @param content Notice message. + * @param [options] Optional notice options. * * @example * ```js @@ -137,9 +115,12 @@ export function createNotice( status = DEFAULT_STATUS, content, options = {} ) { * }; * ``` * - * @return {Object} Action object. + * @return Action object. */ -export function createSuccessNotice( content, options ) { +export function createSuccessNotice( + content: string, + options?: NoticeOptions +) { return createNotice( 'success', content, options ); } @@ -149,8 +130,8 @@ export function createSuccessNotice( content, options ) { * * @see createNotice * - * @param {string} content Notice message. - * @param {Object} [options] Optional notice options. + * @param content Notice message. + * @param [options] Optional notice options. * * @example * ```js @@ -175,9 +156,9 @@ export function createSuccessNotice( content, options ) { * }; *``` * - * @return {Object} Action object. + * @return Action object. */ -export function createInfoNotice( content, options ) { +export function createInfoNotice( content: string, options?: NoticeOptions ) { return createNotice( 'info', content, options ); } @@ -187,8 +168,8 @@ export function createInfoNotice( content, options ) { * * @see createNotice * - * @param {string} content Notice message. - * @param {Object} [options] Optional notice options. + * @param content Notice message. + * @param [options] Optional notice options. * * @example * ```js @@ -216,9 +197,9 @@ export function createInfoNotice( content, options ) { * }; * ``` * - * @return {Object} Action object. + * @return Action object. */ -export function createErrorNotice( content, options ) { +export function createErrorNotice( content: string, options?: NoticeOptions ) { return createNotice( 'error', content, options ); } @@ -228,8 +209,8 @@ export function createErrorNotice( content, options ) { * * @see createNotice * - * @param {string} content Notice message. - * @param {Object} [options] Optional notice options. + * @param content Notice message. + * @param [options] Optional notice options. * * @example * ```js @@ -258,18 +239,21 @@ export function createErrorNotice( content, options ) { * }; * ``` * - * @return {Object} Action object. + * @return Action object. */ -export function createWarningNotice( content, options ) { +export function createWarningNotice( + content: string, + options?: NoticeOptions +) { return createNotice( 'warning', content, options ); } /** * Returns an action object used in signalling that a notice is to be removed. * - * @param {string} id Notice unique identifier. - * @param {string} [context='global'] Optional context (grouping) in which the notice is - * intended to appear. Defaults to default context. + * @param id Notice unique identifier. + * @param [context] Optional context (grouping) in which the notice is + * intended to appear. Defaults to 'default' context. * * @example * ```js @@ -303,9 +287,12 @@ export function createWarningNotice( content, options ) { *}; * ``` * - * @return {Object} Action object. + * @return Action object. */ -export function removeNotice( id, context = DEFAULT_CONTEXT ) { +export function removeNotice( + id: string, + context = DEFAULT_CONTEXT +): Extract< ReducerAction, { type: 'REMOVE_NOTICE' } > { return { type: 'REMOVE_NOTICE', id, @@ -316,8 +303,8 @@ export function removeNotice( id, context = DEFAULT_CONTEXT ) { /** * Removes all notices from a given context. Defaults to the default context. * - * @param {string} noticeType The context to remove all notices from. - * @param {string} context The context to remove all notices from. + * @param noticeType The context to remove all notices from. + * @param context The context to remove all notices from. * * @example * ```js @@ -357,12 +344,12 @@ export function removeNotice( id, context = DEFAULT_CONTEXT ) { * }; * ``` * - * @return {Object} Action object. + * @return Action object. */ export function removeAllNotices( noticeType = 'default', context = DEFAULT_CONTEXT -) { +): Extract< ReducerAction, { type: 'REMOVE_ALL_NOTICES' } > { return { type: 'REMOVE_ALL_NOTICES', noticeType, @@ -373,9 +360,9 @@ export function removeAllNotices( /** * Returns an action object used in signalling that several notices are to be removed. * - * @param {string[]} ids List of unique notice identifiers. - * @param {string} [context='global'] Optional context (grouping) in which the notices are - * intended to appear. Defaults to default context. + * @param ids List of unique notice identifiers. + * @param [context] Optional context (grouping) in which the notices are + * intended to appear. Defaults to 'default' context. * @example * ```js * import { __ } from '@wordpress/i18n'; @@ -406,9 +393,12 @@ export function removeAllNotices( * ); * }; * ``` - * @return {Object} Action object. + * @return Action object. */ -export function removeNotices( ids, context = DEFAULT_CONTEXT ) { +export function removeNotices( + ids: Array< string >, + context = DEFAULT_CONTEXT +): Extract< ReducerAction, { type: 'REMOVE_NOTICES' } > { return { type: 'REMOVE_NOTICES', ids, diff --git a/packages/notices/src/store/constants.js b/packages/notices/src/store/constants.ts similarity index 71% rename from packages/notices/src/store/constants.js rename to packages/notices/src/store/constants.ts index 2949bde0577db..df07e8593e127 100644 --- a/packages/notices/src/store/constants.js +++ b/packages/notices/src/store/constants.ts @@ -1,15 +1,16 @@ +/** + * Internal dependencies + */ +import type { Notice } from './types'; + /** * Default context to use for notice grouping when not otherwise specified. Its * specific value doesn't hold much meaning, but it must be reasonably unique * and, more importantly, referenced consistently in the store implementation. - * - * @type {string} */ export const DEFAULT_CONTEXT = 'global'; /** * Default notice status. - * - * @type {string} */ -export const DEFAULT_STATUS = 'info'; +export const DEFAULT_STATUS: Notice[ 'status' ] = 'info'; diff --git a/packages/notices/src/store/controls.js b/packages/notices/src/store/controls.ts similarity index 67% rename from packages/notices/src/store/controls.js rename to packages/notices/src/store/controls.ts index 80c20900dd5f3..9bc362e52283a 100644 --- a/packages/notices/src/store/controls.js +++ b/packages/notices/src/store/controls.ts @@ -4,7 +4,7 @@ import { speak } from '@wordpress/a11y'; export default { - SPEAK( action ) { + SPEAK( action: { message: string; ariaLive?: 'polite' | 'assertive' } ) { speak( action.message, action.ariaLive || 'assertive' ); }, }; diff --git a/packages/notices/src/store/index.js b/packages/notices/src/store/index.ts similarity index 95% rename from packages/notices/src/store/index.js rename to packages/notices/src/store/index.ts index cab1b3186dfc7..a10f9694b2007 100644 --- a/packages/notices/src/store/index.js +++ b/packages/notices/src/store/index.ts @@ -21,4 +21,6 @@ export const store = createReduxStore( 'core/notices', { selectors, } ); +export * from './types'; + register( store ); diff --git a/packages/notices/src/store/reducer.js b/packages/notices/src/store/reducer.ts similarity index 72% rename from packages/notices/src/store/reducer.js rename to packages/notices/src/store/reducer.ts index 5f4d88c04af29..df97de1eb597e 100644 --- a/packages/notices/src/store/reducer.js +++ b/packages/notices/src/store/reducer.ts @@ -1,18 +1,22 @@ /** * Internal dependencies */ +import type { Notice, ReducerAction } from './types'; import onSubKey from './utils/on-sub-key'; /** * Reducer returning the next notices state. The notices state is an object * where each key is a context, its value an array of notice objects. * - * @param {Object} state Current state. - * @param {Object} action Dispatched action. + * @param state Current state. + * @param action Dispatched action. * - * @return {Object} Updated state. + * @return Updated state. */ -const notices = onSubKey( 'context' )( ( state = [], action ) => { +const notices = onSubKey( 'context' )< Array< Notice >, ReducerAction >( ( + state = [], + action +) => { switch ( action.type ) { case 'CREATE_NOTICE': // Avoid duplicates on ID. @@ -29,9 +33,10 @@ const notices = onSubKey( 'context' )( ( state = [], action ) => { case 'REMOVE_ALL_NOTICES': return state.filter( ( { type } ) => type !== action.noticeType ); - } - return state; + default: + return state; + } } ); export default notices; diff --git a/packages/notices/src/store/selectors.js b/packages/notices/src/store/selectors.js deleted file mode 100644 index d3cd9d41a0ec0..0000000000000 --- a/packages/notices/src/store/selectors.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Internal dependencies - */ -import { DEFAULT_CONTEXT } from './constants'; - -/** @typedef {import('./actions').WPNoticeAction} WPNoticeAction */ - -/** - * The default empty set of notices to return when there are no notices - * assigned for a given notices context. This can occur if the getNotices - * selector is called without a notice ever having been created for the - * context. A shared value is used to ensure referential equality between - * sequential selector calls, since otherwise `[] !== []`. - * - * @type {Array} - */ -const DEFAULT_NOTICES = []; - -/** - * @typedef {Object} WPNotice Notice object. - * - * @property {string} id Unique identifier of notice. - * @property {string} status Status of notice, one of `success`, - * `info`, `error`, or `warning`. Defaults - * to `info`. - * @property {string} content Notice message. - * @property {string} spokenMessage Audibly announced message text used by - * assistive technologies. - * @property {string} __unstableHTML Notice message as raw HTML. Intended to - * serve primarily for compatibility of - * server-rendered notices, and SHOULD NOT - * be used for notices. It is subject to - * removal without notice. - * @property {boolean} isDismissible Whether the notice can be dismissed by - * user. Defaults to `true`. - * @property {string} type Type of notice, one of `default`, - * or `snackbar`. Defaults to `default`. - * @property {boolean} speak Whether the notice content should be - * announced to screen readers. Defaults to - * `true`. - * @property {WPNoticeAction[]} actions User actions to present with notice. - */ - -/** - * Returns all notices as an array, optionally for a given context. Defaults to - * the global context. - * - * @param {Object} state Notices state. - * @param {?string} context Optional grouping context. - * - * @example - * - *```js - * import { useSelect } from '@wordpress/data'; - * import { store as noticesStore } from '@wordpress/notices'; - * - * const ExampleComponent = () => { - * const notices = useSelect( ( select ) => select( noticesStore ).getNotices() ); - * return ( - *
    - * { notices.map( ( notice ) => ( - *
  • { notice.content }
  • - * ) ) } - *
- * ) - * }; - *``` - * - * @return {WPNotice[]} Array of notices. - */ -export function getNotices( state, context = DEFAULT_CONTEXT ) { - return state[ context ] || DEFAULT_NOTICES; -} diff --git a/packages/notices/src/store/selectors.ts b/packages/notices/src/store/selectors.ts new file mode 100644 index 0000000000000..c40a3fa649c88 --- /dev/null +++ b/packages/notices/src/store/selectors.ts @@ -0,0 +1,49 @@ +/** + * Internal dependencies + */ +import { DEFAULT_CONTEXT } from './constants'; +import type { Notice } from './types'; + +/** + * The default empty set of notices to return when there are no notices + * assigned for a given notices context. This can occur if the getNotices + * selector is called without a notice ever having been created for the + * context. A shared value is used to ensure referential equality between + * sequential selector calls, since otherwise `[] !== []`. + * + */ +const DEFAULT_NOTICES: Array< Notice > = []; + +/** + * Returns all notices as an array, optionally for a given context. Defaults to + * the global context. + * + * @param state Notices state. + * @param [context] Optional grouping context. + * + * @example + * + *```js + * import { useSelect } from '@wordpress/data'; + * import { store as noticesStore } from '@wordpress/notices'; + * + * const ExampleComponent = () => { + * const notices = useSelect( ( select ) => select( noticesStore ).getNotices() ); + * return ( + *
    + * { notices.map( ( notice ) => ( + *
  • { notice.content }
  • + * ) ) } + *
+ * ) + * }; + *``` + * + * @return Array of notices. + */ +export function getNotices( + state: Record< string, Array< Notice > >, + context = DEFAULT_CONTEXT +) { + return state[ context ] || DEFAULT_NOTICES; +} diff --git a/packages/notices/src/store/types.ts b/packages/notices/src/store/types.ts new file mode 100644 index 0000000000000..26be99af587d7 --- /dev/null +++ b/packages/notices/src/store/types.ts @@ -0,0 +1,160 @@ +/** + * Object describing a user action option associated with a notice. + */ +export type NoticeAction = { + /** + * Message to use as action label. + */ + label: string; + + /** + * Optional URL of resource if action incurs browser navigation. + */ + url?: string; + + /** + * Optional function to invoke when action is triggered by user. + */ + onClick?: VoidFunction; +}; + +/** + * Notice object. + */ +export type Notice = { + /** + * Unique identifier of notice. + */ + id: string; + + /** + * Status of notice, one of `success`, `info`, `error`, or `warning`. Defaults to `info`. + */ + status: 'success' | 'info' | 'error' | 'warning'; + + /** + * Notice message. + */ + content: string; + + /** + * Audibly announced message text used by assistive technologies. + */ + spokenMessage: string | null; + + /** + * Notice message as raw HTML. Intended to serve primarily for compatibility of server-rendered notices, + * and SHOULD NOT be used for notices. It is subject to removal without notice. + */ + __unstableHTML?: boolean; + + /** + * Whether the notice can be dismissed by user. Defaults to `true`. + */ + isDismissible: boolean; + + /** + * Whether the notice includes an explicit dismiss button and can't be dismissed by clicking the body of the notice. Only applies when type is `snackbar`. + */ + explicitDismiss?: boolean; + + /** + * Called when the notice is dismissed. + */ + onDismiss?: VoidFunction; + + /** + * Type of notice, one of `default`, or `snackbar`. + * + * @default 'default' + */ + type: 'default' | 'snackbar' | ( string & {} ); + + /** + * User actions to present with notice. + */ + actions: NoticeAction[]; + + /** + * An icon displayed with the notice. Only used when type is `snackbar`. + */ + icon?: string | null; +}; + +export type NoticeOptions = { + /** + * Context under which to group notice. + * @default 'global' + */ + context?: string; + + /** + * Identifier for notice. Automatically assigned if not specified. + */ + id?: string; + + /** + * Whether the notice can be dismissed by user. + * @default true + */ + isDismissible?: boolean; + + /** + * Type of notice, one of `default`, or `snackbar`. + * @default 'default' + */ + type?: 'default' | 'snackbar' | ( string & {} ); + + /** + * Whether the notice content should be announced to screen readers. + * @default true + */ + speak?: boolean; + + /** + * User actions to be presented with notice. + */ + actions?: NoticeAction[]; + + /** + * An icon displayed with the notice. Only used when type is set to `snackbar`. + */ + icon?: string | null; + + /** + * Whether the notice includes an explicit dismiss button and can't be dismissed by clicking the body of the notice. Only applies when type is set to `snackbar`. + */ + explicitDismiss?: boolean; + + /** + * Called when the notice is dismissed. + */ + onDismiss?: VoidFunction; + + /** + * Notice message as raw HTML. Intended to serve primarily for compatibility of server-rendered notices, + * and SHOULD NOT be used for notices. It is subject to removal without notice. + */ + __unstableHTML?: boolean; +}; + +export type ReducerAction = { + context: string; +} & ( + | { + type: 'CREATE_NOTICE'; + notice: Notice; + } + | { + type: 'REMOVE_NOTICE'; + id: string; + } + | { + type: 'REMOVE_NOTICES'; + ids: Array< string >; + } + | { + type: 'REMOVE_ALL_NOTICES'; + noticeType: string; + } +); diff --git a/packages/notices/src/store/utils/on-sub-key.js b/packages/notices/src/store/utils/on-sub-key.ts similarity index 67% rename from packages/notices/src/store/utils/on-sub-key.js rename to packages/notices/src/store/utils/on-sub-key.ts index 663c7ea3e779d..d7f732e4b830d 100644 --- a/packages/notices/src/store/utils/on-sub-key.js +++ b/packages/notices/src/store/utils/on-sub-key.ts @@ -1,15 +1,22 @@ +/** + * External dependencies + */ +import type { Reducer, Action } from 'redux'; + /** * Higher-order reducer creator which creates a combined reducer object, keyed * by a property on the action object. * - * @param {string} actionProperty Action property by which to key object. + * @param actionProperty Action property by which to key object. * - * @return {Function} Higher-order reducer. + * @return Higher-order reducer. */ export const onSubKey = - ( actionProperty ) => - ( reducer ) => - ( state = {}, action ) => { + ( actionProperty: string ) => + < S, A extends Action & Record< string, any > >( + reducer: Reducer< S, A > + ) => + ( state: Record< string, S > = {}, action: A ) => { // Retrieve subkey from action. Do not track if undefined; useful for cases // where reducer is scoped by action shape. const key = action[ actionProperty ]; diff --git a/packages/notices/tsconfig.json b/packages/notices/tsconfig.json index e36a6fe9f4eb6..13bcc1dbc28b2 100644 --- a/packages/notices/tsconfig.json +++ b/packages/notices/tsconfig.json @@ -8,5 +8,5 @@ "checkJs": false }, "references": [ { "path": "../a11y" }, { "path": "../data" } ], - "include": [ "src/**/*" ] + "include": [ "src" ] } From c20b1acf462f0a3c5defe316be25575facb5aea6 Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Wed, 4 Dec 2024 14:48:23 +0530 Subject: [PATCH 2/6] Convert unit tests to TS --- .../src/store/test/{actions.js => actions.ts} | 4 ++-- .../src/store/test/{reducer.js => reducer.ts} | 1 + .../store/test/{selectors.js => selectors.ts} | 17 +++++++++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) rename packages/notices/src/store/test/{actions.js => actions.ts} (98%) rename packages/notices/src/store/test/{reducer.js => reducer.ts} (99%) rename packages/notices/src/store/test/{selectors.js => selectors.ts} (76%) diff --git a/packages/notices/src/store/test/actions.js b/packages/notices/src/store/test/actions.ts similarity index 98% rename from packages/notices/src/store/test/actions.js rename to packages/notices/src/store/test/actions.ts index 37fefc5c3558f..17fca19b58eaf 100644 --- a/packages/notices/src/store/test/actions.js +++ b/packages/notices/src/store/test/actions.ts @@ -16,7 +16,7 @@ import { DEFAULT_CONTEXT, DEFAULT_STATUS } from '../constants'; describe( 'actions', () => { describe( 'createNotice', () => { const id = 'my-id'; - const status = 'status'; + const status = 'success'; const content = 'my message'; it( 'returns an action when options is empty', () => { @@ -37,7 +37,7 @@ describe( 'actions', () => { } ); it( 'normalizes content to string', () => { - const result = createNotice( status, Hello ); + const result = createNotice( status, 'Hello' ); expect( result ).toMatchObject( { type: 'CREATE_NOTICE', diff --git a/packages/notices/src/store/test/reducer.js b/packages/notices/src/store/test/reducer.ts similarity index 99% rename from packages/notices/src/store/test/reducer.js rename to packages/notices/src/store/test/reducer.ts index d807b814a51f4..c9e6d5a2bd6b0 100644 --- a/packages/notices/src/store/test/reducer.js +++ b/packages/notices/src/store/test/reducer.ts @@ -18,6 +18,7 @@ import { DEFAULT_CONTEXT } from '../constants'; describe( 'reducer', () => { it( 'should default to an empty object', () => { + // @ts-expect-error Testing for an empty object. const state = reducer( undefined, {} ); expect( state ).toEqual( {} ); diff --git a/packages/notices/src/store/test/selectors.js b/packages/notices/src/store/test/selectors.ts similarity index 76% rename from packages/notices/src/store/test/selectors.js rename to packages/notices/src/store/test/selectors.ts index b470282d05c08..eb688309d88e9 100644 --- a/packages/notices/src/store/test/selectors.js +++ b/packages/notices/src/store/test/selectors.ts @@ -21,7 +21,12 @@ describe( 'selectors', () => { ], }; - expect( getNotices( state ) ).toEqual( [ + expect( + getNotices( + // @ts-expect-error Testing partial state + state + ) + ).toEqual( [ { id: 'b', content: 'message 1' }, { id: 'a', content: 'message 2' }, ] ); @@ -32,9 +37,13 @@ describe( 'selectors', () => { foo: [ { id: 'c', content: 'message 3' } ], }; - expect( getNotices( state, 'foo' ) ).toEqual( [ - { id: 'c', content: 'message 3' }, - ] ); + expect( + getNotices( + // @ts-expect-error Testing partial state + state, + 'foo' + ) + ).toEqual( [ { id: 'c', content: 'message 3' } ] ); } ); } ); } ); From dd0115e18bcbf047696b007d0837e5f1bd8373c9 Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Wed, 4 Dec 2024 16:37:02 +0530 Subject: [PATCH 3/6] Update CHANGELOG.md --- packages/notices/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/notices/CHANGELOG.md b/packages/notices/CHANGELOG.md index 68c4ef7f85ab5..a488541eab975 100644 --- a/packages/notices/CHANGELOG.md +++ b/packages/notices/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Enhancements + +- Improved TypeScript definitions ([#67565](https://github.com/WordPress/gutenberg/pull/67565)). + ## 5.13.0 (2024-11-27) ## 5.12.0 (2024-11-16) From 78b4c53d50ad130070446e74f9ef31bb7c8fbcd6 Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Wed, 4 Dec 2024 16:43:07 +0530 Subject: [PATCH 4/6] Clean up typedef --- packages/notices/src/store/actions.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/notices/src/store/actions.ts b/packages/notices/src/store/actions.ts index 46ca982e71394..ffc29c5819394 100644 --- a/packages/notices/src/store/actions.ts +++ b/packages/notices/src/store/actions.ts @@ -4,12 +4,6 @@ import { DEFAULT_CONTEXT, DEFAULT_STATUS } from './constants'; import type { NoticeOptions, ReducerAction } from './types'; -/** - * @typedef {import('./types').NoticeAction} NoticeAction Notice action object. - * - * @typedef {import('./types').NoticeOptions} NoticeOptions Notice action object. - */ - let uniqueId = 0; /** From daf2a8696ebfcff5a2422fc948e47b21bb6a9fd3 Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Fri, 6 Dec 2024 20:08:18 +0530 Subject: [PATCH 5/6] Set explicit type for context etc. for auto-generated docs --- .../data/data-core-notices.md | 18 ++++---- packages/notices/src/store/actions.ts | 42 +++++++++---------- packages/notices/src/store/selectors.ts | 6 +-- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/docs/reference-guides/data/data-core-notices.md b/docs/reference-guides/data/data-core-notices.md index 7bdf15b3dde53..61ef45ff13a8f 100644 --- a/docs/reference-guides/data/data-core-notices.md +++ b/docs/reference-guides/data/data-core-notices.md @@ -33,7 +33,7 @@ const ExampleComponent = () => { _Parameters_ - _state_ `Record< string, Array< Notice > >`: Notices state. -- _context_ Optional grouping context. +- _context_ `string`: Optional grouping context. _Returns_ @@ -83,7 +83,7 @@ const ExampleComponent = () => { _Parameters_ - _content_ `string`: Notice message. -- _options_ `[NoticeOptions]`: Optional notice options. +- _options_ `NoticeOptions`: Optional notice options. _Returns_ @@ -124,7 +124,7 @@ const ExampleComponent = () => { _Parameters_ - _content_ `string`: Notice message. -- _options_ `[NoticeOptions]`: Optional notice options. +- _options_ `NoticeOptions`: Optional notice options. _Returns_ @@ -158,7 +158,7 @@ _Parameters_ - _status_ Notice status ("info" if undefined is passed). - _content_ `string`: Notice message. -- _options_ `[NoticeOptions]`: Notice options. +- _options_ `NoticeOptions`: Optional notice options. _Returns_ @@ -200,7 +200,7 @@ const ExampleComponent = () => { _Parameters_ - _content_ `string`: Notice message. -- _options_ `[NoticeOptions]`: Optional notice options. +- _options_ `NoticeOptions`: Optional notice options. _Returns_ @@ -246,7 +246,7 @@ const ExampleComponent = () => { _Parameters_ - _content_ `string`: Notice message. -- _options_ `[NoticeOptions]`: Optional notice options. +- _options_ `NoticeOptions`: Optional notice options. _Returns_ @@ -293,7 +293,7 @@ export const ExampleComponent = () => { _Parameters_ - _noticeType_ The context to remove all notices from. -- _context_ The context to remove all notices from. +- _context_ `string`: The optional context to remove all notices from. _Returns_ @@ -341,7 +341,7 @@ const ExampleComponent = () => { _Parameters_ - _id_ `string`: Notice unique identifier. -- _context_ Optional context (grouping) in which the notice is intended to appear. Defaults to 'default' context. +- _context_ `string`: Optional context (grouping) in which the notice is intended to appear. Defaults to 'default' context. _Returns_ @@ -386,7 +386,7 @@ const ExampleComponent = () => { _Parameters_ - _ids_ `Array< string >`: List of unique notice identifiers. -- _context_ Optional context (grouping) in which the notices are intended to appear. Defaults to 'default' context. +- _context_ `string`: Optional context (grouping) in which the notices are intended to appear. Defaults to 'default' context. _Returns_ diff --git a/packages/notices/src/store/actions.ts b/packages/notices/src/store/actions.ts index ffc29c5819394..c82339032e36f 100644 --- a/packages/notices/src/store/actions.ts +++ b/packages/notices/src/store/actions.ts @@ -9,9 +9,9 @@ let uniqueId = 0; /** * Returns an action object used in signalling that a notice is to be created. * - * @param status Notice status ("info" if undefined is passed). - * @param content Notice message. - * @param [options] Notice options. + * @param status Notice status ("info" if undefined is passed). + * @param content Notice message. + * @param options Optional notice options. * * @example * ```js @@ -82,8 +82,8 @@ export function createNotice( * * @see createNotice * - * @param content Notice message. - * @param [options] Optional notice options. + * @param content Notice message. + * @param options Optional notice options. * * @example * ```js @@ -124,8 +124,8 @@ export function createSuccessNotice( * * @see createNotice * - * @param content Notice message. - * @param [options] Optional notice options. + * @param content Notice message. + * @param options Optional notice options. * * @example * ```js @@ -162,8 +162,8 @@ export function createInfoNotice( content: string, options?: NoticeOptions ) { * * @see createNotice * - * @param content Notice message. - * @param [options] Optional notice options. + * @param content Notice message. + * @param options Optional notice options. * * @example * ```js @@ -203,8 +203,8 @@ export function createErrorNotice( content: string, options?: NoticeOptions ) { * * @see createNotice * - * @param content Notice message. - * @param [options] Optional notice options. + * @param content Notice message. + * @param options Optional notice options. * * @example * ```js @@ -245,9 +245,9 @@ export function createWarningNotice( /** * Returns an action object used in signalling that a notice is to be removed. * - * @param id Notice unique identifier. - * @param [context] Optional context (grouping) in which the notice is - * intended to appear. Defaults to 'default' context. + * @param id Notice unique identifier. + * @param context Optional context (grouping) in which the notice is + * intended to appear. Defaults to 'default' context. * * @example * ```js @@ -285,7 +285,7 @@ export function createWarningNotice( */ export function removeNotice( id: string, - context = DEFAULT_CONTEXT + context: string = DEFAULT_CONTEXT ): Extract< ReducerAction, { type: 'REMOVE_NOTICE' } > { return { type: 'REMOVE_NOTICE', @@ -298,7 +298,7 @@ export function removeNotice( * Removes all notices from a given context. Defaults to the default context. * * @param noticeType The context to remove all notices from. - * @param context The context to remove all notices from. + * @param context The optional context to remove all notices from. * * @example * ```js @@ -342,7 +342,7 @@ export function removeNotice( */ export function removeAllNotices( noticeType = 'default', - context = DEFAULT_CONTEXT + context: string = DEFAULT_CONTEXT ): Extract< ReducerAction, { type: 'REMOVE_ALL_NOTICES' } > { return { type: 'REMOVE_ALL_NOTICES', @@ -354,9 +354,9 @@ export function removeAllNotices( /** * Returns an action object used in signalling that several notices are to be removed. * - * @param ids List of unique notice identifiers. - * @param [context] Optional context (grouping) in which the notices are - * intended to appear. Defaults to 'default' context. + * @param ids List of unique notice identifiers. + * @param context Optional context (grouping) in which the notices are + * intended to appear. Defaults to 'default' context. * @example * ```js * import { __ } from '@wordpress/i18n'; @@ -391,7 +391,7 @@ export function removeAllNotices( */ export function removeNotices( ids: Array< string >, - context = DEFAULT_CONTEXT + context: string = DEFAULT_CONTEXT ): Extract< ReducerAction, { type: 'REMOVE_NOTICES' } > { return { type: 'REMOVE_NOTICES', diff --git a/packages/notices/src/store/selectors.ts b/packages/notices/src/store/selectors.ts index c40a3fa649c88..d136aff780f0c 100644 --- a/packages/notices/src/store/selectors.ts +++ b/packages/notices/src/store/selectors.ts @@ -18,8 +18,8 @@ const DEFAULT_NOTICES: Array< Notice > = []; * Returns all notices as an array, optionally for a given context. Defaults to * the global context. * - * @param state Notices state. - * @param [context] Optional grouping context. + * @param state Notices state. + * @param context Optional grouping context. * * @example * @@ -43,7 +43,7 @@ const DEFAULT_NOTICES: Array< Notice > = []; */ export function getNotices( state: Record< string, Array< Notice > >, - context = DEFAULT_CONTEXT + context: string = DEFAULT_CONTEXT ) { return state[ context ] || DEFAULT_NOTICES; } From 55701ba091717486274aa05a09c9b9d7bc1f3999 Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Fri, 27 Dec 2024 17:47:22 +0530 Subject: [PATCH 6/6] No need "include" in tsconfig --- packages/notices/tsconfig.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/notices/tsconfig.json b/packages/notices/tsconfig.json index ed6d3f7900a56..9c48147297764 100644 --- a/packages/notices/tsconfig.json +++ b/packages/notices/tsconfig.json @@ -5,6 +5,5 @@ "types": [ "gutenberg-env" ], "checkJs": false }, - "references": [ { "path": "../a11y" }, { "path": "../data" } ], - "include": [ "src" ] + "references": [ { "path": "../a11y" }, { "path": "../data" } ] }