Migrate @woocommerce/notice to TS
This commit is contained in:
parent
2885a0ae86
commit
286358d724
|
@ -20,6 +20,7 @@
|
||||||
},
|
},
|
||||||
"main": "build/index.js",
|
"main": "build/index.js",
|
||||||
"module": "build-module/index.js",
|
"module": "build-module/index.js",
|
||||||
|
"types": "build-types",
|
||||||
"react-native": "src/index",
|
"react-native": "src/index",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wordpress/a11y": "^3.5.0",
|
"@wordpress/a11y": "^3.5.0",
|
||||||
|
@ -45,11 +46,14 @@
|
||||||
"lint:fix": "eslint src --fix"
|
"lint:fix": "eslint src --fix"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@automattic/data-stores": "^2.0.1",
|
||||||
"@babel/core": "^7.17.5",
|
"@babel/core": "^7.17.5",
|
||||||
"@woocommerce/eslint-plugin": "workspace:*",
|
"@woocommerce/eslint-plugin": "workspace:*",
|
||||||
|
"@woocommerce/data": "workspace:*",
|
||||||
"eslint": "^8.12.0",
|
"eslint": "^8.12.0",
|
||||||
"jest": "^27.5.1",
|
"jest": "^27.5.1",
|
||||||
"jest-cli": "^27.5.1",
|
"jest-cli": "^27.5.1",
|
||||||
|
"redux": "^4.2.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"ts-jest": "^27.1.3",
|
"ts-jest": "^27.1.3",
|
||||||
"typescript": "^4.6.2"
|
"typescript": "^4.6.2"
|
||||||
|
|
|
@ -2,22 +2,25 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { uniqueId } from 'lodash';
|
import { uniqueId } from 'lodash';
|
||||||
|
import { Status, Action as WPNoticeAction } from '@wordpress/notices';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { DEFAULT_CONTEXT, DEFAULT_STATUS } from './constants';
|
import { DEFAULT_CONTEXT, DEFAULT_STATUS } from './constants';
|
||||||
|
|
||||||
/**
|
export type Options = {
|
||||||
* @typedef {Object} WPNoticeAction Object describing a user action option associated with a notice.
|
id: string;
|
||||||
*
|
context: string;
|
||||||
* @property {string} label Message to use as action label.
|
isDismissible: boolean;
|
||||||
* @property {?string} url Optional URL of resource if action incurs
|
type: string;
|
||||||
* browser navigation.
|
speak: boolean;
|
||||||
* @property {?Function} onClick Optional function to invoke when action is
|
actions: Array< WPNoticeAction >;
|
||||||
* triggered by user.
|
icon: null | JSX.Element;
|
||||||
*
|
explicitDismiss: boolean;
|
||||||
*/
|
onDismiss: ( () => void ) | null;
|
||||||
|
__unstableHTML?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an action object used in signalling that a notice is to be created.
|
* Returns an action object used in signalling that a notice is to be created.
|
||||||
|
@ -46,30 +49,43 @@ import { DEFAULT_CONTEXT, DEFAULT_STATUS } from './constants';
|
||||||
* can't be dismissed by clicking
|
* can't be dismissed by clicking
|
||||||
* the body of the notice.
|
* the body of the notice.
|
||||||
* @param {Function} [options.onDismiss] Called when the notice is dismissed.
|
* @param {Function} [options.onDismiss] Called when the notice is dismissed.
|
||||||
|
* @param {boolean} [options.__unstableHTML] Notice message as raw HTML.
|
||||||
*
|
*
|
||||||
* @return {Object} Action object.
|
* @return {Object} WPNoticeAction object.
|
||||||
*/
|
*/
|
||||||
export function createNotice( status = DEFAULT_STATUS, content, options = {} ) {
|
export function createNotice(
|
||||||
const {
|
status: Status = DEFAULT_STATUS,
|
||||||
speak = true,
|
content: string,
|
||||||
isDismissible = true,
|
{
|
||||||
context = DEFAULT_CONTEXT,
|
speak,
|
||||||
id = uniqueId( context ),
|
isDismissible,
|
||||||
actions = [],
|
context,
|
||||||
type = 'default',
|
id,
|
||||||
|
actions,
|
||||||
|
type,
|
||||||
__unstableHTML,
|
__unstableHTML,
|
||||||
icon = null,
|
icon,
|
||||||
explicitDismiss = false,
|
explicitDismiss,
|
||||||
onDismiss = null,
|
onDismiss,
|
||||||
} = options;
|
}: Options = {
|
||||||
|
speak: true,
|
||||||
|
isDismissible: true,
|
||||||
|
context: DEFAULT_CONTEXT,
|
||||||
|
id: uniqueId( DEFAULT_CONTEXT ),
|
||||||
|
actions: [],
|
||||||
|
type: 'default',
|
||||||
|
icon: null,
|
||||||
|
explicitDismiss: false,
|
||||||
|
onDismiss: null,
|
||||||
|
}
|
||||||
|
) {
|
||||||
// The supported value shape of content is currently limited to plain text
|
// The supported value shape of content is currently limited to plain text
|
||||||
// strings. To avoid setting expectation that e.g. a WPElement could be
|
// strings. To avoid setting expectation that e.g. a WPElement could be
|
||||||
// supported, cast to a string.
|
// supported, cast to a string.
|
||||||
content = String( content );
|
content = String( content );
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'CREATE_NOTICE',
|
type: 'CREATE_NOTICE' as const,
|
||||||
context,
|
context,
|
||||||
notice: {
|
notice: {
|
||||||
id,
|
id,
|
||||||
|
@ -98,7 +114,7 @@ export function createNotice( status = DEFAULT_STATUS, content, options = {} ) {
|
||||||
*
|
*
|
||||||
* @return {Object} Action object.
|
* @return {Object} Action object.
|
||||||
*/
|
*/
|
||||||
export function createSuccessNotice( content, options ) {
|
export function createSuccessNotice( content: string, options: Options ) {
|
||||||
return createNotice( 'success', content, options );
|
return createNotice( 'success', content, options );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +129,7 @@ export function createSuccessNotice( content, options ) {
|
||||||
*
|
*
|
||||||
* @return {Object} Action object.
|
* @return {Object} Action object.
|
||||||
*/
|
*/
|
||||||
export function createInfoNotice( content, options ) {
|
export function createInfoNotice( content: string, options: Options ) {
|
||||||
return createNotice( 'info', content, options );
|
return createNotice( 'info', content, options );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +144,7 @@ export function createInfoNotice( content, options ) {
|
||||||
*
|
*
|
||||||
* @return {Object} Action object.
|
* @return {Object} Action object.
|
||||||
*/
|
*/
|
||||||
export function createErrorNotice( content, options ) {
|
export function createErrorNotice( content: string, options: Options ) {
|
||||||
return createNotice( 'error', content, options );
|
return createNotice( 'error', content, options );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +159,7 @@ export function createErrorNotice( content, options ) {
|
||||||
*
|
*
|
||||||
* @return {Object} Action object.
|
* @return {Object} Action object.
|
||||||
*/
|
*/
|
||||||
export function createWarningNotice( content, options ) {
|
export function createWarningNotice( content: string, options: Options ) {
|
||||||
return createNotice( 'warning', content, options );
|
return createNotice( 'warning', content, options );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,10 +172,12 @@ export function createWarningNotice( content, options ) {
|
||||||
*
|
*
|
||||||
* @return {Object} Action object.
|
* @return {Object} Action object.
|
||||||
*/
|
*/
|
||||||
export function removeNotice( id, context = DEFAULT_CONTEXT ) {
|
export function removeNotice( id: string, context: string = DEFAULT_CONTEXT ) {
|
||||||
return {
|
return {
|
||||||
type: 'REMOVE_NOTICE',
|
type: 'REMOVE_NOTICE' as const,
|
||||||
id,
|
id,
|
||||||
context,
|
context,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Action = ReturnType< typeof createNotice | typeof removeNotice >;
|
|
@ -12,4 +12,4 @@ export const DEFAULT_CONTEXT = 'global';
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
export const DEFAULT_STATUS = 'info';
|
export const DEFAULT_STATUS = 'info' as const;
|
|
@ -3,8 +3,13 @@
|
||||||
*/
|
*/
|
||||||
import { speak } from '@wordpress/a11y';
|
import { speak } from '@wordpress/a11y';
|
||||||
|
|
||||||
|
export type Action = {
|
||||||
|
message: string;
|
||||||
|
ariaLive?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
SPEAK( action ) {
|
SPEAK( action: Action ) {
|
||||||
speak( action.message, action.ariaLive || 'assertive' );
|
speak( action.message, action.ariaLive || 'assertive' );
|
||||||
},
|
},
|
||||||
};
|
};
|
|
@ -2,6 +2,8 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { registerStore } from '@wordpress/data';
|
import { registerStore } from '@wordpress/data';
|
||||||
|
import { SelectFromMap, DispatchFromMap } from '@automattic/data-stores';
|
||||||
|
import { Reducer, AnyAction } from 'redux';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -9,11 +11,24 @@ import { registerStore } from '@wordpress/data';
|
||||||
import reducer from './reducer';
|
import reducer from './reducer';
|
||||||
import * as actions from './actions';
|
import * as actions from './actions';
|
||||||
import * as selectors from './selectors';
|
import * as selectors from './selectors';
|
||||||
|
import { State } from './types';
|
||||||
|
export * from './types';
|
||||||
|
|
||||||
|
export const STORE_NAME = 'core/notices2';
|
||||||
// NOTE: This uses core/notices2, if this file is copied back upstream
|
// NOTE: This uses core/notices2, if this file is copied back upstream
|
||||||
// to Gutenberg this needs to be changed back to core/notices.
|
// to Gutenberg this needs to be changed back to core/notices.
|
||||||
export default registerStore( 'core/notices2', {
|
export default registerStore< State >( STORE_NAME, {
|
||||||
reducer,
|
reducer: reducer as Reducer< State, AnyAction >,
|
||||||
actions,
|
actions,
|
||||||
selectors,
|
selectors,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
declare module '@wordpress/data' {
|
||||||
|
// TODO: convert action.js to TS
|
||||||
|
function dispatch(
|
||||||
|
key: typeof STORE_NAME
|
||||||
|
): DispatchFromMap< typeof actions >;
|
||||||
|
function select(
|
||||||
|
key: typeof STORE_NAME
|
||||||
|
): SelectFromMap< typeof selectors >;
|
||||||
|
}
|
||||||
|
|
|
@ -2,22 +2,24 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { reject } from 'lodash';
|
import { reject } from 'lodash';
|
||||||
|
import type { Reducer } from 'redux';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import onSubKey from './utils/on-sub-key';
|
import onSubKey from './utils/on-sub-key';
|
||||||
|
import { Action } from './actions';
|
||||||
|
import { Notices } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reducer returning the next notices state. The notices state is an object
|
* Reducer returning the next notices state. The notices state is an array of notice objects
|
||||||
* where each key is a context, its value an array of notice objects.
|
|
||||||
*
|
*
|
||||||
* @param {Object} state Current state.
|
* @param {Array} state Current state.
|
||||||
* @param {Object} action Dispatched action.
|
* @param {Object} action Dispatched action.
|
||||||
*
|
*
|
||||||
* @return {Object} Updated state.
|
* @return {Object} Updated state.
|
||||||
*/
|
*/
|
||||||
const notices = onSubKey( 'context' )( ( state = [], action ) => {
|
const notices: Reducer< Notices, Action > = ( state = [], action ) => {
|
||||||
switch ( action.type ) {
|
switch ( action.type ) {
|
||||||
case 'CREATE_NOTICE':
|
case 'CREATE_NOTICE':
|
||||||
// Avoid duplicates on ID.
|
// Avoid duplicates on ID.
|
||||||
|
@ -29,8 +31,12 @@ const notices = onSubKey( 'context' )( ( state = [], action ) => {
|
||||||
case 'REMOVE_NOTICE':
|
case 'REMOVE_NOTICE':
|
||||||
return reject( state, { id: action.id } );
|
return reject( state, { id: action.id } );
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
} );
|
};
|
||||||
|
|
||||||
export default notices;
|
export type State = {
|
||||||
|
[ context: string ]: Notices;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Creates a combined reducer object where each key is a context, its value an array of notice objects.
|
||||||
|
export default onSubKey( 'context' )( notices );
|
|
@ -2,6 +2,7 @@
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { DEFAULT_CONTEXT } from './constants';
|
import { DEFAULT_CONTEXT } from './constants';
|
||||||
|
import { State } from './reducer';
|
||||||
|
|
||||||
/** @typedef {import('./actions').WPNoticeAction} WPNoticeAction */
|
/** @typedef {import('./actions').WPNoticeAction} WPNoticeAction */
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ import { DEFAULT_CONTEXT } from './constants';
|
||||||
*
|
*
|
||||||
* @type {Array}
|
* @type {Array}
|
||||||
*/
|
*/
|
||||||
const DEFAULT_NOTICES = [];
|
const DEFAULT_NOTICES: [ ] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} WPNotice Notice object.
|
* @typedef {Object} WPNotice Notice object.
|
||||||
|
@ -51,6 +52,6 @@ const DEFAULT_NOTICES = [];
|
||||||
*
|
*
|
||||||
* @return {WPNotice[]} Array of notices.
|
* @return {WPNotice[]} Array of notices.
|
||||||
*/
|
*/
|
||||||
export function getNotices( state, context = DEFAULT_CONTEXT ) {
|
export function getNotices( state: State, context: string = DEFAULT_CONTEXT ) {
|
||||||
return state[ context ] || DEFAULT_NOTICES;
|
return state[ context ] || DEFAULT_NOTICES;
|
||||||
}
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { createNotice } from './actions';
|
||||||
|
|
||||||
|
export type Notices = Array< ReturnType< typeof createNotice >[ 'notice' ] >;
|
||||||
|
|
||||||
|
export type State = {
|
||||||
|
[ context: string ]: Notices;
|
||||||
|
};
|
|
@ -1,3 +1,14 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import type { Reducer } from 'redux';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { State, Notices } from '../types';
|
||||||
|
import { Action } from '../actions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Higher-order reducer creator which creates a combined reducer object, keyed
|
* Higher-order reducer creator which creates a combined reducer object, keyed
|
||||||
* by a property on the action object.
|
* by a property on the action object.
|
||||||
|
@ -6,10 +17,9 @@
|
||||||
*
|
*
|
||||||
* @return {Function} Higher-order reducer.
|
* @return {Function} Higher-order reducer.
|
||||||
*/
|
*/
|
||||||
export const onSubKey = ( actionProperty ) => ( reducer ) => (
|
export const onSubKey = ( actionProperty: keyof Action ) => (
|
||||||
state = {},
|
reducer: Reducer< Notices, Action >
|
||||||
action
|
) => ( state: State = {}, action: Action ) => {
|
||||||
) => {
|
|
||||||
// Retrieve subkey from action. Do not track if undefined; useful for cases
|
// Retrieve subkey from action. Do not track if undefined; useful for cases
|
||||||
// where reducer is scoped by action shape.
|
// where reducer is scoped by action shape.
|
||||||
const key = action[ actionProperty ];
|
const key = action[ actionProperty ];
|
|
@ -2,6 +2,9 @@
|
||||||
"extends": "../tsconfig",
|
"extends": "../tsconfig",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"outDir": "build-module"
|
"outDir": "build-module",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"declarationDir": "./build-types"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue