diff --git a/packages/js/data/changelog/add-33443_shipping_zones_data_store b/packages/js/data/changelog/add-33443_shipping_zones_data_store new file mode 100644 index 00000000000..9c33d89ea05 --- /dev/null +++ b/packages/js/data/changelog/add-33443_shipping_zones_data_store @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add new data store for shipping zones. #33830 diff --git a/packages/js/data/src/crud/reducer.ts b/packages/js/data/src/crud/reducer.ts index 283b4ce27ae..767975da27d 100644 --- a/packages/js/data/src/crud/reducer.ts +++ b/packages/js/data/src/crud/reducer.ts @@ -43,7 +43,7 @@ export const createReducer = () => { ...state.errors, [ getResourceName( payload.errorType, - payload.query as ItemQuery + ( payload.query || {} ) as ItemQuery ) ]: payload.error, }, }; @@ -114,7 +114,7 @@ export const createReducer = () => { const itemQuery = getResourceName( CRUD_ACTIONS.GET_ITEMS, - payload.query as ItemQuery + ( payload.query || {} ) as ItemQuery ); return { diff --git a/packages/js/data/src/crud/resolvers.ts b/packages/js/data/src/crud/resolvers.ts index c21ae92fa29..e24f524eb3c 100644 --- a/packages/js/data/src/crud/resolvers.ts +++ b/packages/js/data/src/crud/resolvers.ts @@ -41,9 +41,9 @@ export const createResolvers = ( { } }; - const getItems = function* ( query: Partial< ItemQuery > ) { + const getItems = function* ( query?: Partial< ItemQuery > ) { // Require ID when requesting specific fields to later update the resource data. - const resourceQuery = { ...query }; + const resourceQuery = query ? { ...query } : {}; if ( resourceQuery && diff --git a/packages/js/data/src/crud/selectors.ts b/packages/js/data/src/crud/selectors.ts index d3bd793d5c3..61f687cd429 100644 --- a/packages/js/data/src/crud/selectors.ts +++ b/packages/js/data/src/crud/selectors.ts @@ -39,8 +39,11 @@ export const getItemError = ( state: ResourceState, id: IdType ) => { }; export const getItems = createSelector( - ( state: ResourceState, query: ItemQuery ) => { - const itemQuery = getResourceName( CRUD_ACTIONS.GET_ITEMS, query ); + ( state: ResourceState, query?: ItemQuery ) => { + const itemQuery = getResourceName( + CRUD_ACTIONS.GET_ITEMS, + query || {} + ); const ids = state.items[ itemQuery ] ? state.items[ itemQuery ].data @@ -50,7 +53,7 @@ export const getItems = createSelector( return null; } - if ( query._fields ) { + if ( query && query._fields ) { return ids.map( ( id: IdType ) => { return query._fields.reduce( ( item: Partial< Item >, field: string ) => { @@ -71,7 +74,10 @@ export const getItems = createSelector( .filter( ( item ) => item !== undefined ); }, ( state, query ) => { - const itemQuery = getResourceName( CRUD_ACTIONS.GET_ITEMS, query ); + const itemQuery = getResourceName( + CRUD_ACTIONS.GET_ITEMS, + query || {} + ); const ids = state.items[ itemQuery ] ? state.items[ itemQuery ].data : undefined; @@ -84,8 +90,8 @@ export const getItems = createSelector( } ); -export const getItemsError = ( state: ResourceState, query: ItemQuery ) => { - const itemQuery = getResourceName( CRUD_ACTIONS.GET_ITEMS, query ); +export const getItemsError = ( state: ResourceState, query?: ItemQuery ) => { + const itemQuery = getResourceName( CRUD_ACTIONS.GET_ITEMS, query || {} ); return state.errors[ itemQuery ]; }; diff --git a/packages/js/data/src/crud/types.ts b/packages/js/data/src/crud/types.ts index ed3ce19451b..5143746f899 100644 --- a/packages/js/data/src/crud/types.ts +++ b/packages/js/data/src/crud/types.ts @@ -105,7 +105,7 @@ export type CrudSelectors< export type MapSelectors< Type, ResourceName, ParamType, ReturnType > = { [ Property in keyof Type as `get${ Capitalize< string & ResourceName - > }${ Capitalize< string & Property > }` ]: ( x: ParamType ) => ReturnType; + > }${ Capitalize< string & Property > }` ]: ( x?: ParamType ) => ReturnType; }; export type MapActions< Type, ResourceName, ParamType, ReturnType > = { diff --git a/packages/js/data/src/index.ts b/packages/js/data/src/index.ts index d57cab0bf1e..2bdf1f64c85 100644 --- a/packages/js/data/src/index.ts +++ b/packages/js/data/src/index.ts @@ -20,6 +20,7 @@ export { PRODUCTS_STORE_NAME } from './products'; export { ORDERS_STORE_NAME } from './orders'; export { PRODUCT_ATTRIBUTES_STORE_NAME } from './product-attributes'; export { EXPERIMENTAL_PRODUCT_SHIPPING_CLASSES_STORE_NAME } from './product-shipping-classes'; +export { EXPERIMENTAL_SHIPPING_ZONES_STORE_NAME } from './shipping-zones'; export { PaymentGateway } from './payment-gateways/types'; // Export hooks @@ -93,6 +94,7 @@ import type { PRODUCTS_STORE_NAME } from './products'; import type { ORDERS_STORE_NAME } from './orders'; import type { PRODUCT_ATTRIBUTES_STORE_NAME } from './product-attributes'; import type { EXPERIMENTAL_PRODUCT_SHIPPING_CLASSES_STORE_NAME } from './product-shipping-classes'; +import type { EXPERIMENTAL_SHIPPING_ZONES_STORE_NAME } from './shipping-zones'; export type WCDataStoreName = | typeof REVIEWS_STORE_NAME @@ -110,7 +112,8 @@ export type WCDataStoreName = | typeof PRODUCTS_STORE_NAME | typeof ORDERS_STORE_NAME | typeof PRODUCT_ATTRIBUTES_STORE_NAME - | typeof EXPERIMENTAL_PRODUCT_SHIPPING_CLASSES_STORE_NAME; + | typeof EXPERIMENTAL_PRODUCT_SHIPPING_CLASSES_STORE_NAME + | typeof EXPERIMENTAL_SHIPPING_ZONES_STORE_NAME; /** * Internal dependencies @@ -124,6 +127,7 @@ import { ProductsSelectors } from './products/selectors'; import { OrdersSelectors } from './orders/selectors'; import { ProductAttributeSelectors } from './product-attributes/types'; import { ProductShippingClassSelectors } from './product-shipping-classes/types'; +import { ShippingZonesSelectors } from './shipping-zones/types'; // As we add types to all the package selectors we can fill out these unknown types with real ones. See one // of the already typed selectors for an example of how you can do this. @@ -159,6 +163,8 @@ export type WCSelectorType< T > = T extends typeof REVIEWS_STORE_NAME ? ProductShippingClassSelectors : T extends typeof ORDERS_STORE_NAME ? OrdersSelectors + : T extends typeof EXPERIMENTAL_SHIPPING_ZONES_STORE_NAME + ? ShippingZonesSelectors : never; export interface WCDataSelector { @@ -170,3 +176,4 @@ export { ActionDispatchers as PluginsStoreActions } from './plugins/actions'; export { ActionDispatchers as ProductAttributesActions } from './product-attributes/types'; export { ActionDispatchers as ProductsStoreActions } from './products/actions'; export { ActionDispatchers as ProductShippingClassesActions } from './product-shipping-classes/types'; +export { ActionDispatchers as ShippingZonesActions } from './shipping-zones/types'; diff --git a/packages/js/data/src/shipping-zones/README.md b/packages/js/data/src/shipping-zones/README.md new file mode 100644 index 00000000000..5b2540de53e --- /dev/null +++ b/packages/js/data/src/shipping-zones/README.md @@ -0,0 +1,46 @@ +# Shipping Zones Data Store + +This data store provides functions to interact with the [Shipping Zones REST endpoints](https://woocommerce.github.io/woocommerce-rest-api-docs/#shipping-zones). +Under the hood this data store makes use of the [CRUD data store](../crud/README.md). + +**Note: This data store is listed as experimental still as it is still in active development.** + +## Usage + +This data store can be accessed under the `experimental/wc/admin/shipping/zones` name. It is recommended you make use of the export constant `EXPERIMENTAL_SHIPPING_ZONES_STORE_NAME`. + +Example: + +```ts +import { + EXPERIMENTAL_SHIPPING_ZONES_STORE_NAME, + ShippingZonesActions, +} from '@woocommerce/data'; +import { useDispatch } from '@wordpress/data'; + +function Component() { + const actions = useDispatch( + EXPERIMENTAL_SHIPPING_ZONES_STORE_NAME + ) as ShippingZonesActions; + actions.createShippingZone( { name: 'test' } ); +} +``` + +## Selections and actions: + +| Selector | Description | +| -------------------------------------- | ------------------------------------------------------- | +| `getShippingZone( id: number )` | Gets a Shipping Zone by ID | +| `getShippingZoneError( id )` | Get the error for a failing GET shipping zone request. | +| `getShippingZones( query = {} )` | Get all shipping zones, query object is empty. | +| `getShippingZoneesError( query = {} )` | Get the error for a GET request for all shipping zones. | + +Example usage: `wp.data.select( EXPERIMENTAL_SHIPPING_ZONES_STORE_NAME ).getShippingZone( 3 );` + +| Actions | Method | Description | +| ----------------------------------------------- | ------ | ------------------------------------------------------------------------- | +| `createShippingZone( shippingZoneObject )` | POST | Creates shipping zone, see `ShippingZone` [here](./types.ts) for values | +| `deleteShippingZone( id )` | DELETE | Deletes a shipping class by ID | +| `updatetShippingZone( id, shippingZoneObject )` | PUT | Updates a shipping zone, see `ShippingZone` [here](./types.ts) for values | + +Example usage: `wp.data.dispatch( EXPERIMENTAL_SHIPPING_ZONES_STORE_NAME ).updateShippingZone( 3, { name: 'New name' } );` diff --git a/packages/js/data/src/shipping-zones/constants.ts b/packages/js/data/src/shipping-zones/constants.ts new file mode 100644 index 00000000000..f9c847e51a1 --- /dev/null +++ b/packages/js/data/src/shipping-zones/constants.ts @@ -0,0 +1,3 @@ +export const STORE_NAME = 'experimental/wc/admin/shipping/zones'; + +export const WC_SHIPPING_ZONES_NAMESPACE = '/wc/v3/shipping/zones'; diff --git a/packages/js/data/src/shipping-zones/index.ts b/packages/js/data/src/shipping-zones/index.ts new file mode 100644 index 00000000000..e7b45c8a1c8 --- /dev/null +++ b/packages/js/data/src/shipping-zones/index.ts @@ -0,0 +1,14 @@ +/** + * Internal dependencies + */ +import { STORE_NAME, WC_SHIPPING_ZONES_NAMESPACE } from './constants'; +import { createCrudDataStore } from '../crud'; + +createCrudDataStore( { + storeName: STORE_NAME, + resourceName: 'ShippingZone', + pluralResourceName: 'ShippingZones', + namespace: WC_SHIPPING_ZONES_NAMESPACE, +} ); + +export const EXPERIMENTAL_SHIPPING_ZONES_STORE_NAME = STORE_NAME; diff --git a/packages/js/data/src/shipping-zones/types.ts b/packages/js/data/src/shipping-zones/types.ts new file mode 100644 index 00000000000..4008831f474 --- /dev/null +++ b/packages/js/data/src/shipping-zones/types.ts @@ -0,0 +1,36 @@ +/** + * External dependencies + */ +import { DispatchFromMap } from '@automattic/data-stores'; + +/** + * Internal dependencies + */ +import { CrudActions, CrudSelectors } from '../crud/types'; + +type ShippingZone = { + id: number; + name: string; + order: number; +}; + +type ReadOnlyProperties = 'id'; + +type MutableProperties = Omit< ShippingZone, ReadOnlyProperties >; + +type ShippingZonesActions = CrudActions< + 'ShippingZone', + ShippingZone, + MutableProperties, + 'name' +>; + +export type ShippingZonesSelectors = CrudSelectors< + 'ShippingZone', + 'ShippingZones', + ShippingZone, + undefined, + MutableProperties +>; + +export type ActionDispatchers = DispatchFromMap< ShippingZonesActions >;