Migrate @woocommerce/data navigation store to TS (#34239)
* Migrate woo data navigation store to TS * Add changelog * Fix action type * Update packages/js/data/src/navigation/reducer.ts Co-authored-by: Ilyas Foo <foo.ilyas@gmail.com> * Add a doc comment to MenuItem type Co-authored-by: Ilyas Foo <foo.ilyas@gmail.com>
This commit is contained in:
parent
a6d656f363
commit
02b47a67db
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: dev
|
||||
|
||||
Migrate navigation store to TS
|
|
@ -11,6 +11,6 @@ const TYPES = {
|
|||
REMOVE_FAVORITE_FAILURE: 'REMOVE_FAVORITE_FAILURE',
|
||||
REMOVE_FAVORITE_REQUEST: 'REMOVE_FAVORITE_REQUEST',
|
||||
REMOVE_FAVORITE_SUCCESS: 'REMOVE_FAVORITE_SUCCESS',
|
||||
};
|
||||
} as const;
|
||||
|
||||
export default TYPES;
|
|
@ -9,50 +9,51 @@ import { getPersistedQuery } from '@woocommerce/navigation';
|
|||
*/
|
||||
import TYPES from './action-types';
|
||||
import { WC_ADMIN_NAMESPACE } from '../constants';
|
||||
import { MenuItem } from './types';
|
||||
|
||||
export function setMenuItems( menuItems ) {
|
||||
export function setMenuItems( menuItems: MenuItem[] ) {
|
||||
return {
|
||||
type: TYPES.SET_MENU_ITEMS,
|
||||
menuItems,
|
||||
};
|
||||
}
|
||||
|
||||
export function addMenuItems( menuItems ) {
|
||||
export function addMenuItems( menuItems: MenuItem[] ) {
|
||||
return {
|
||||
type: TYPES.ADD_MENU_ITEMS,
|
||||
menuItems,
|
||||
};
|
||||
}
|
||||
|
||||
export function getFavoritesFailure( error ) {
|
||||
export function getFavoritesFailure( error: unknown ) {
|
||||
return {
|
||||
type: TYPES.GET_FAVORITES_FAILURE,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
export function getFavoritesRequest( favorites ) {
|
||||
export function getFavoritesRequest( favorites?: string[] ) {
|
||||
return {
|
||||
type: TYPES.GET_FAVORITES_REQUEST,
|
||||
favorites,
|
||||
};
|
||||
}
|
||||
|
||||
export function getFavoritesSuccess( favorites ) {
|
||||
export function getFavoritesSuccess( favorites: string[] ) {
|
||||
return {
|
||||
type: TYPES.GET_FAVORITES_SUCCESS,
|
||||
favorites,
|
||||
};
|
||||
}
|
||||
|
||||
export function addFavoriteRequest( favorite ) {
|
||||
export function addFavoriteRequest( favorite: string ) {
|
||||
return {
|
||||
type: TYPES.ADD_FAVORITE_REQUEST,
|
||||
favorite,
|
||||
};
|
||||
}
|
||||
|
||||
export function addFavoriteFailure( favorite, error ) {
|
||||
export function addFavoriteFailure( favorite: string, error: unknown ) {
|
||||
return {
|
||||
type: TYPES.ADD_FAVORITE_FAILURE,
|
||||
favorite,
|
||||
|
@ -60,21 +61,21 @@ export function addFavoriteFailure( favorite, error ) {
|
|||
};
|
||||
}
|
||||
|
||||
export function addFavoriteSuccess( favorite ) {
|
||||
export function addFavoriteSuccess( favorite: string ) {
|
||||
return {
|
||||
type: TYPES.ADD_FAVORITE_SUCCESS,
|
||||
favorite,
|
||||
};
|
||||
}
|
||||
|
||||
export function removeFavoriteRequest( favorite ) {
|
||||
export function removeFavoriteRequest( favorite: string ) {
|
||||
return {
|
||||
type: TYPES.REMOVE_FAVORITE_REQUEST,
|
||||
favorite,
|
||||
};
|
||||
}
|
||||
|
||||
export function removeFavoriteFailure( favorite, error ) {
|
||||
export function removeFavoriteFailure( favorite: string, error: unknown ) {
|
||||
return {
|
||||
type: TYPES.REMOVE_FAVORITE_FAILURE,
|
||||
favorite,
|
||||
|
@ -82,18 +83,13 @@ export function removeFavoriteFailure( favorite, error ) {
|
|||
};
|
||||
}
|
||||
|
||||
export function removeFavoriteSuccess( favorite, error ) {
|
||||
export function removeFavoriteSuccess( favorite: string ) {
|
||||
return {
|
||||
type: TYPES.REMOVE_FAVORITE_SUCCESS,
|
||||
favorite,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
export function* onLoad() {
|
||||
yield onHistoryChange();
|
||||
}
|
||||
|
||||
export function* onHistoryChange() {
|
||||
const persistedQuery = getPersistedQuery();
|
||||
|
||||
|
@ -107,11 +103,15 @@ export function* onHistoryChange() {
|
|||
};
|
||||
}
|
||||
|
||||
export function* addFavorite( favorite ) {
|
||||
export function* onLoad() {
|
||||
yield onHistoryChange();
|
||||
}
|
||||
|
||||
export function* addFavorite( favorite: string ) {
|
||||
yield addFavoriteRequest( favorite );
|
||||
|
||||
try {
|
||||
const results = yield apiFetch( {
|
||||
const results: string[] = yield apiFetch( {
|
||||
path: `${ WC_ADMIN_NAMESPACE }/navigation/favorites/me`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
|
@ -131,11 +131,11 @@ export function* addFavorite( favorite ) {
|
|||
}
|
||||
}
|
||||
|
||||
export function* removeFavorite( favorite ) {
|
||||
export function* removeFavorite( favorite: string ) {
|
||||
yield removeFavoriteRequest( favorite );
|
||||
|
||||
try {
|
||||
const results = yield apiFetch( {
|
||||
const results: string[] = yield apiFetch( {
|
||||
path: `${ WC_ADMIN_NAMESPACE }/navigation/favorites/me`,
|
||||
method: 'DELETE',
|
||||
data: {
|
||||
|
@ -154,3 +154,21 @@ export function* removeFavorite( favorite ) {
|
|||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export type Action = ReturnType<
|
||||
| typeof setMenuItems
|
||||
| typeof addMenuItems
|
||||
| typeof getFavoritesFailure
|
||||
| typeof getFavoritesRequest
|
||||
| typeof getFavoritesSuccess
|
||||
| typeof addFavoriteRequest
|
||||
| typeof addFavoriteFailure
|
||||
| typeof addFavoriteSuccess
|
||||
| typeof removeFavoriteRequest
|
||||
| typeof removeFavoriteFailure
|
||||
| typeof removeFavoriteSuccess
|
||||
| ( () => {
|
||||
type: typeof TYPES.ON_HISTORY_CHANGE;
|
||||
persistedQuery: ReturnType< typeof getPersistedQuery >;
|
||||
} )
|
||||
>;
|
|
@ -1 +1 @@
|
|||
export const STORE_NAME = 'woocommerce-navigation';
|
||||
export const STORE_NAME = 'woocommerce-navigation' as const;
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { controls } from '@wordpress/data-controls';
|
||||
import { registerStore } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_NAME } from './constants';
|
||||
import * as selectors from './selectors';
|
||||
import * as actions from './actions';
|
||||
import reducer from './reducer';
|
||||
import * as resolvers from './resolvers';
|
||||
import initDispatchers from './dispatchers';
|
||||
|
||||
registerStore( STORE_NAME, {
|
||||
reducer,
|
||||
actions,
|
||||
controls,
|
||||
resolvers,
|
||||
selectors,
|
||||
} );
|
||||
|
||||
initDispatchers();
|
||||
export const NAVIGATION_STORE_NAME = STORE_NAME;
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { registerStore } from '@wordpress/data';
|
||||
import { controls } from '@wordpress/data-controls';
|
||||
import { SelectFromMap, DispatchFromMap } from '@automattic/data-stores';
|
||||
import { Reducer, AnyAction } from 'redux';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_NAME } from './constants';
|
||||
import * as selectors from './selectors';
|
||||
import * as actions from './actions';
|
||||
import reducer, { State } from './reducer';
|
||||
import * as resolvers from './resolvers';
|
||||
import initDispatchers from './dispatchers';
|
||||
import { WPDataActions, WPDataSelectors } from '../types';
|
||||
|
||||
registerStore< State >( STORE_NAME, {
|
||||
reducer: reducer as Reducer< State, AnyAction >,
|
||||
actions,
|
||||
controls,
|
||||
resolvers,
|
||||
selectors,
|
||||
} );
|
||||
|
||||
initDispatchers();
|
||||
export const NAVIGATION_STORE_NAME = STORE_NAME;
|
||||
|
||||
declare module '@wordpress/data' {
|
||||
function dispatch(
|
||||
key: typeof STORE_NAME
|
||||
): DispatchFromMap< typeof actions & WPDataActions >;
|
||||
function select(
|
||||
key: typeof STORE_NAME
|
||||
): SelectFromMap< typeof selectors > & WPDataSelectors;
|
||||
}
|
|
@ -1,9 +1,16 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import type { Reducer } from 'redux';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
import { Action } from './actions';
|
||||
import { NavigationState } from './types';
|
||||
|
||||
const reducer = (
|
||||
const reducer: Reducer< NavigationState, Action > = (
|
||||
state = {
|
||||
error: null,
|
||||
menuItems: [],
|
||||
|
@ -11,25 +18,25 @@ const reducer = (
|
|||
requesting: {},
|
||||
persistedQuery: {},
|
||||
},
|
||||
{ type, error, favorite, favorites, menuItems, persistedQuery }
|
||||
action
|
||||
) => {
|
||||
switch ( type ) {
|
||||
switch ( action.type ) {
|
||||
case TYPES.SET_MENU_ITEMS:
|
||||
state = {
|
||||
...state,
|
||||
menuItems,
|
||||
menuItems: action.menuItems,
|
||||
};
|
||||
break;
|
||||
case TYPES.ADD_MENU_ITEMS:
|
||||
state = {
|
||||
...state,
|
||||
menuItems: [ ...state.menuItems, ...menuItems ],
|
||||
menuItems: [ ...state.menuItems, ...action.menuItems ],
|
||||
};
|
||||
break;
|
||||
case TYPES.ON_HISTORY_CHANGE:
|
||||
state = {
|
||||
...state,
|
||||
persistedQuery,
|
||||
persistedQuery: action.persistedQuery,
|
||||
};
|
||||
break;
|
||||
case TYPES.GET_FAVORITES_FAILURE:
|
||||
|
@ -53,7 +60,7 @@ const reducer = (
|
|||
case TYPES.GET_FAVORITES_SUCCESS:
|
||||
state = {
|
||||
...state,
|
||||
favorites,
|
||||
favorites: action.favorites,
|
||||
requesting: {
|
||||
...state.requesting,
|
||||
getFavorites: false,
|
||||
|
@ -63,7 +70,7 @@ const reducer = (
|
|||
case TYPES.ADD_FAVORITE_FAILURE:
|
||||
state = {
|
||||
...state,
|
||||
error,
|
||||
error: action.error,
|
||||
requesting: {
|
||||
...state.requesting,
|
||||
addFavorite: false,
|
||||
|
@ -80,15 +87,15 @@ const reducer = (
|
|||
};
|
||||
break;
|
||||
case TYPES.ADD_FAVORITE_SUCCESS:
|
||||
const newFavorites = ! state.favorites.includes( favorite )
|
||||
? [ ...state.favorites, favorite ]
|
||||
const newFavorites = ! state.favorites.includes( action.favorite )
|
||||
? [ ...state.favorites, action.favorite ]
|
||||
: state.favorites;
|
||||
|
||||
state = {
|
||||
...state,
|
||||
favorites: newFavorites,
|
||||
menuItems: state.menuItems.map( ( item ) => {
|
||||
if ( item.id === favorite ) {
|
||||
if ( item.id === action.favorite ) {
|
||||
return {
|
||||
...item,
|
||||
menuId: 'favorites',
|
||||
|
@ -107,7 +114,7 @@ const reducer = (
|
|||
...state,
|
||||
requesting: {
|
||||
...state.requesting,
|
||||
error,
|
||||
error: action.error,
|
||||
removeFavorite: false,
|
||||
},
|
||||
};
|
||||
|
@ -123,14 +130,14 @@ const reducer = (
|
|||
break;
|
||||
case TYPES.REMOVE_FAVORITE_SUCCESS:
|
||||
const filteredFavorites = state.favorites.filter(
|
||||
( f ) => f !== favorite
|
||||
( f ) => f !== action.favorite
|
||||
);
|
||||
|
||||
state = {
|
||||
...state,
|
||||
favorites: filteredFavorites,
|
||||
menuItems: state.menuItems.map( ( item ) => {
|
||||
if ( item.id === favorite ) {
|
||||
if ( item.id === action.favorite ) {
|
||||
return {
|
||||
...item,
|
||||
menuId: 'plugins',
|
||||
|
@ -148,4 +155,5 @@ const reducer = (
|
|||
return state;
|
||||
};
|
||||
|
||||
export type State = ReturnType< typeof reducer >;
|
||||
export default reducer;
|
|
@ -17,7 +17,7 @@ export function* getFavorites() {
|
|||
yield getFavoritesRequest();
|
||||
|
||||
try {
|
||||
const results = yield apiFetch( {
|
||||
const results: string[] = yield apiFetch( {
|
||||
path: `${ WC_ADMIN_NAMESPACE }/navigation/favorites/me`,
|
||||
} );
|
||||
|
|
@ -3,9 +3,14 @@
|
|||
*/
|
||||
import { applyFilters } from '@wordpress/hooks';
|
||||
|
||||
const MENU_ITEMS_HOOK = 'woocommerce_navigation_menu_items';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { NavigationState } from './types';
|
||||
|
||||
export const getMenuItems = ( state ) => {
|
||||
const MENU_ITEMS_HOOK = 'woocommerce_navigation_menu_items' as const;
|
||||
|
||||
export const getMenuItems = ( state: NavigationState ) => {
|
||||
/**
|
||||
* Navigation Menu Items.
|
||||
*
|
||||
|
@ -15,14 +20,17 @@ export const getMenuItems = ( state ) => {
|
|||
return applyFilters( MENU_ITEMS_HOOK, state.menuItems );
|
||||
};
|
||||
|
||||
export const getFavorites = ( state ) => {
|
||||
export const getFavorites = ( state: NavigationState ) => {
|
||||
return state.favorites || [];
|
||||
};
|
||||
|
||||
export const isNavigationRequesting = ( state, selector ) => {
|
||||
export const isNavigationRequesting = (
|
||||
state: NavigationState,
|
||||
selector: string
|
||||
) => {
|
||||
return state.requesting[ selector ] || false;
|
||||
};
|
||||
|
||||
export const getPersistedQuery = ( state ) => {
|
||||
export const getPersistedQuery = ( state: NavigationState ) => {
|
||||
return state.persistedQuery || {};
|
||||
};
|
|
@ -14,6 +14,7 @@ const defaultState = {
|
|||
|
||||
describe( 'navigation reducer', () => {
|
||||
it( 'should return a default state', () => {
|
||||
// @ts-expect-error -- we're testing the reducer's default state.
|
||||
const state = reducer( undefined, {} );
|
||||
expect( state ).toEqual( defaultState );
|
||||
expect( state ).not.toBe( defaultState );
|
||||
|
@ -27,16 +28,25 @@ describe( 'navigation reducer', () => {
|
|||
id: 'menu-item-1',
|
||||
title: 'Menu Item 1',
|
||||
menuId: 'primary',
|
||||
url: 'https://example.com/menu-item-1',
|
||||
migrate: true,
|
||||
order: 1,
|
||||
},
|
||||
{
|
||||
id: 'menu-item-2',
|
||||
title: 'Menu Item 2',
|
||||
menuId: 'primary',
|
||||
url: 'https://example.com/menu-item-2',
|
||||
migrate: true,
|
||||
order: 2,
|
||||
},
|
||||
{
|
||||
id: 'menu-item-3',
|
||||
title: 'Menu Item 3',
|
||||
menuId: 'secondary',
|
||||
url: 'https://example.com/menu-item-3',
|
||||
migrate: true,
|
||||
order: 3,
|
||||
},
|
||||
],
|
||||
} );
|
||||
|
@ -55,8 +65,15 @@ describe( 'navigation reducer', () => {
|
|||
id: 'menu-item-1',
|
||||
title: 'Menu Item 1',
|
||||
menuId: 'primary',
|
||||
url: 'https://example.com/menu-item-1',
|
||||
migrate: true,
|
||||
order: 1,
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
favorites: [],
|
||||
requesting: {},
|
||||
persistedQuery: {},
|
||||
},
|
||||
{
|
||||
type: TYPES.ADD_MENU_ITEMS,
|
||||
|
@ -65,6 +82,9 @@ describe( 'navigation reducer', () => {
|
|||
id: 'menu-item-2',
|
||||
title: 'Menu Item 2',
|
||||
menuId: 'primary',
|
||||
url: 'https://example.com/menu-item-2',
|
||||
migrate: true,
|
||||
order: 2,
|
||||
},
|
||||
],
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { getPersistedQuery } from '@woocommerce/navigation';
|
||||
|
||||
// https://github.com/woocommerce/woocommerce/blob/ecec9eaa76cb7b2e36f79175837b71f4f64996b1/plugins/woocommerce/src/Admin/Features/Navigation/Menu.php
|
||||
export type MenuItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
url: string;
|
||||
order: number;
|
||||
migrate: boolean;
|
||||
menuId: string;
|
||||
isCategory?: boolean;
|
||||
badge?: number;
|
||||
backButtonLabel?: string;
|
||||
parent?: string;
|
||||
capability?: string;
|
||||
matchExpression?: string;
|
||||
};
|
||||
|
||||
export type NavigationState = {
|
||||
error: null | unknown;
|
||||
menuItems: MenuItem[];
|
||||
favorites: string[];
|
||||
requesting: {
|
||||
[ key: string ]: boolean | unknown;
|
||||
};
|
||||
persistedQuery: ReturnType< typeof getPersistedQuery >;
|
||||
};
|
|
@ -9,17 +9,20 @@ import { createElement, useRef } from '@wordpress/element';
|
|||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_NAME } from './constants';
|
||||
import { MenuItem } from './types';
|
||||
|
||||
/**
|
||||
* Higher-order component used to hydrate navigation data.
|
||||
*
|
||||
* @param {Object} data Data object with menu items and site information.
|
||||
* @param {MenuItem[]} data.menuItems Menu items to hydrate.
|
||||
*/
|
||||
export const withNavigationHydration = ( data ) =>
|
||||
createHigherOrderComponent(
|
||||
export const withNavigationHydration = ( data: { menuItems: MenuItem[] } ) =>
|
||||
createHigherOrderComponent< Record< string, unknown > >(
|
||||
( OriginalComponent ) => ( props ) => {
|
||||
const dataRef = useRef( data );
|
||||
|
||||
// @ts-expect-error // @ts-expect-error registry is not defined in the wp.data typings
|
||||
useSelect( ( select, registry ) => {
|
||||
if ( ! dataRef.current ) {
|
||||
return;
|
Loading…
Reference in New Issue