* Rename the argument in the CheckoutFilterFunction type

This only exists as an extra descriptive hint to anyone using this type, the value `label` was never used by anything so it does not need to be changed anywhere else.

* Create Available Filters document

* Add available filters to the extensibility README

* Update docs/extensibility/available-filters.md to fix typographical error

Co-authored-by: Mike Jolley <mike.jolley@me.com>

Co-authored-by: Mike Jolley <mike.jolley@me.com>
This commit is contained in:
Thomas Roberts 2021-05-05 10:41:48 +01:00 committed by GitHub
parent 7a53dd1f27
commit 57b4215bce
3 changed files with 157 additions and 2 deletions

View File

@ -7,4 +7,5 @@ These documents are all dealing with extensibility in the various WooCommerce Bl
| Document | Description |
| ---------- | ---------- |
[Payment Method Integration](./payment-method-integration.md) | Information about implementing payment methods.
[Checkout Flow and Events](./checkout-flow-and-events.md) | All about the checkout flow in the checkout block and the various emitted events that can be subscribed to.
[Checkout Flow and Events](./checkout-flow-and-events.md) | All about the checkout flow in the checkout block and the various emitted events that can be subscribed to.
[Available Filters](./available-filters.md) | All about the filters that you may use to change values of certain elements of WooCommerce Blocks.

View File

@ -0,0 +1,154 @@
# Filters
Like traditional WordPress filters (you register a callback with a specific filter, your callback accepts a number of
arguments, then it returns a value), we are introducing filters to the WooCommerce Blocks extension. These will function
very similarly to the traditional filters.
Your extension will use `__experimentalRegisterCheckoutFilter` to set up a filter.
This function has the following signature:
```typescript
(
namespace: string,
filters: Record< string, CheckoutFilterFunction >
)
```
and a `CheckoutFilterFunction` has this signature:
```typescript
type CheckoutFilterFunction = < T >(
value: T,
extensions: Record< string, unknown >,
args?: CheckoutFilterArguments
) => T;
```
In this, you'll specify which filter you want to work with (available filters are listed below) and the
function (`CheckoutFilterFunction`) to execute when this filter is applied.
When the `CheckoutFilterFunction` is invoked, the following arguments are passed to it:
- `value` - The value to be filtered.
- `extensions` A n object containing extension data. If your extension has extended any of the store's API routes, one
of the keys of this object will be your extension's namespace. The value will contain any data you add to the endpoint.
Each key in the `extensions` object is an extension namespace, so a third party extension cannot interfere with _your_
extension's schema modifications, unless there is a naming collision, so please ensure your extension has a unique
namespace that is unlikely to conflict with other extensions.
- `args` - An object containing any additional data passed to the filter function. This usually (but not always) contains at least a key
called `context`. The value of `context` will be (at the moment) either `cart` or `checkout`. This is provided to inform
extensions about the exact location that the filter is being applied. The same filter can be applied in multiple
places.
## Available filters
This section of the document will list the filters that are currently available to extensions, where exactly
the filter is applied, and what data might be passed to the `CheckoutFilterFunction`.
### Cart Line Items
Line items refers to each item listed in the cart or checkout. For instance
the "Sunglasses" and "Beanie with logo" in this image are the line items.
<img src="https://user-images.githubusercontent.com/5656702/117027554-b7c3eb00-acf4-11eb-8af1-b8bedbe20e05.png" width=600 />
The following filters are available for line items:
| Filter name | Description | Return type |
|---|---|---|
| `itemName` | Used to change the name of the item before it is rendered onto the page | `string`
| `cartItemPrice` | This is the price of the item, multiplied by the number of items in the cart. | `string` and **must** contain the substring `<price/>` where the price should appear.
| `subtotalPriceFormat` | This is the price of a single item. Irrespective of the number in the cart, this value will always be the current price of _one_ item. | `string` and **must** contain the substring `<price/>` where the price should appear.
| `saleBadgePriceFormat` | This is amount of money saved when buying this item. It is the difference between the item's regular price and its sale price. | `string` and **must** contain the substring `<price/>` where the price should appear.
Each of these filters has the following arguments passed to it: `{ context: 'cart', cartItem: CartItem }` ([CartItem](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/c00da597efe4c16fcf5481c213d8052ec5df3766/assets/js/type-defs/cart.ts#L113))
### Order Summary Items
In the Checkout block, there is a sidebar that contains a summary of what the customer is about to purchase.
There are some filters available to modify the way certain elements are displayed on each item.
The sale badges are not shown here, so those filters are not applied in the Order Summary.
<img src="https://user-images.githubusercontent.com/5656702/117026942-1b014d80-acf4-11eb-8515-b9b777d96a74.png" width=400 />
| Filter name | Description | Return type |
|---|---|---|
| `itemName` | Used to change the name of the item before it is rendered onto the page | `string`
| `cartItemPrice` | This is the price of the item, multiplied by the number of items in the cart. | `string` and **must** contain the substring `<price/>` where the price should appear.
| `subtotalPriceFormat` | This is the price of a single item. Irrespective of the number in the cart, this value will always be the current price of _one_ item. | `string` and **must** contain the substring `<price/>` where the price should appear.
Each of these filters has the following additional arguments passed to it: `{ context: 'summary', cartItem: CartItem }` ([CartItem](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/c00da597efe4c16fcf5481c213d8052ec5df3766/assets/js/type-defs/cart.ts#L113))
### Totals footer item (in Cart and Checkout)
The word 'Total' that precedes the amount due, present in both the Cart _and_ Checkout blocks, is also passed through filters.
| Filter name | Description | Return type |
|---|---|---|
| `totalLabel` | This is the label for the cart total. It defaults to 'Total' (or the word for 'Total' if using translations). | `string`
There are no additional arguments passed to this filter.
## Examples
### Changing the wording of the Totals label in the Cart and Checkout
For this example, let's suppose we are building an extension that lets customers pay a deposit, and defer the full amount until a later date.
To make it easier to understand what the customer is paying and why, let's change the value of `Total` to `Deposit due today`.
1. We need to create a `CheckoutFilterFunction`.
```typescript
const replaceTotalWithDeposit = () => 'Deposit due today';
```
2. Now we need to register this filter function, and have it executed when the `totalLabel` filter is applied.
We can access the `__experimentalRegisterCheckoutFilters` function on the `window.wc.blocksCheckout` object.
As long as your extension's script is enqueued _after_ WooCommerce Blocks' scripts (i.e. by registering `wc-blocks-checkout` as a dependency), then this will be available.
```typescript
const { __experimentalRegisterCheckoutFilters } = window.wc.blocksCheckout;
__experimentalRegisterCheckoutFilters( 'my-hypothetical-deposit-plugin', {
totalLabel: replaceTotalWithDeposit
} );
```
| Before | After |
|---|---|
| <img src="https://user-images.githubusercontent.com/5656702/117032889-cc56b200-acf9-11eb-9bf7-ae5f6a0b1538.png" width=300 /> | <img src="https://user-images.githubusercontent.com/5656702/117033039-ec867100-acf9-11eb-95d5-50c06bf2923c.png" width=300 /> |
### Changing the format of the item's single price
Let's say we want to add a little bit of text after an item's single price **in the Cart only**, just to make sure our customers know
that's the price per item.
1. We will need to register a function to be executed when the `subtotalPriceFormat` is applied. Since we only want this to happen in the
Cart context, our function will need to check the additional arguments passed to it to ensure the `context` value is `cart`.
We can see from the table above, that our function needs to return a string that contains a substring of `<price/>`.
This is a placeholder for the numeric value. The Cart block will interpolate the value into the string we return.
```typescript
const appendTextToPriceInCart = ( value, extensions, args ) => {
if( args?.context !== 'cart') {
// Return early since this filter is not being applied in the Cart context.
// We must return the original value we received here.
return value;
}
return '<price/> per item';
};
```
2. Now we must register it. Refer to the first example for information about `__experimentalRegisterCheckoutFilters`.
```typescript
const { __experimentalRegisterCheckoutFilters } = window.wc.blocksCheckout;
__experimentalRegisterCheckoutFilters( 'my-hypothetical-price-plugin', {
subtotalPriceFormat: appendTextToPriceInCart
} );
```
| Before | After |
|---|---|
| <img src="https://user-images.githubusercontent.com/5656702/117035086-d5488300-acfb-11eb-9954-feb326916168.png" width=400 /> | <img src="https://user-images.githubusercontent.com/5656702/117035616-70415d00-acfc-11eb-98d3-6c8096817e5b.png" width=400 /> |
## Troubleshooting
If you are logged in to the store as an administrator, you should be shown an error like this if your filter is not
working correctly.
<img src="https://user-images.githubusercontent.com/5656702/117035848-b4ccf880-acfc-11eb-870a-31ae86dd6496.png" width=600 />
The error will also be shown in your console.

View File

@ -11,7 +11,7 @@ import { CURRENT_USER_IS_ADMIN } from '@woocommerce/settings';
import { returnTrue } from '../utils';
type CheckoutFilterFunction = < T >(
label: T,
value: T,
extensions: Record< string, unknown >,
args?: CheckoutFilterArguments
) => T;