Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.

Commit c13f04f

Browse files
oprmikejolley
andauthored
Add documentation for filters (#4167)
* 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 <[email protected]> Co-authored-by: Mike Jolley <[email protected]>
1 parent e5c5d1e commit c13f04f

File tree

3 files changed

+157
-2
lines changed

3 files changed

+157
-2
lines changed

docs/extensibility/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ These documents are all dealing with extensibility in the various WooCommerce Bl
77
| Document | Description |
88
| ---------- | ---------- |
99
[Payment Method Integration](./payment-method-integration.md) | Information about implementing payment methods.
10-
[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.
10+
[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.
11+
[Available Filters](./available-filters.md) | All about the filters that you may use to change values of certain elements of WooCommerce Blocks.
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# Filters
2+
Like traditional WordPress filters (you register a callback with a specific filter, your callback accepts a number of
3+
arguments, then it returns a value), we are introducing filters to the WooCommerce Blocks extension. These will function
4+
very similarly to the traditional filters.
5+
6+
Your extension will use `__experimentalRegisterCheckoutFilter` to set up a filter.
7+
8+
This function has the following signature:
9+
10+
```typescript
11+
(
12+
namespace: string,
13+
filters: Record< string, CheckoutFilterFunction >
14+
)
15+
```
16+
17+
and a `CheckoutFilterFunction` has this signature:
18+
19+
```typescript
20+
type CheckoutFilterFunction = < T >(
21+
value: T,
22+
extensions: Record< string, unknown >,
23+
args?: CheckoutFilterArguments
24+
) => T;
25+
```
26+
27+
In this, you'll specify which filter you want to work with (available filters are listed below) and the
28+
function (`CheckoutFilterFunction`) to execute when this filter is applied.
29+
30+
When the `CheckoutFilterFunction` is invoked, the following arguments are passed to it:
31+
- `value` - The value to be filtered.
32+
- `extensions` A n object containing extension data. If your extension has extended any of the store's API routes, one
33+
of the keys of this object will be your extension's namespace. The value will contain any data you add to the endpoint.
34+
Each key in the `extensions` object is an extension namespace, so a third party extension cannot interfere with _your_
35+
extension's schema modifications, unless there is a naming collision, so please ensure your extension has a unique
36+
namespace that is unlikely to conflict with other extensions.
37+
- `args` - An object containing any additional data passed to the filter function. This usually (but not always) contains at least a key
38+
called `context`. The value of `context` will be (at the moment) either `cart` or `checkout`. This is provided to inform
39+
extensions about the exact location that the filter is being applied. The same filter can be applied in multiple
40+
places.
41+
42+
## Available filters
43+
44+
This section of the document will list the filters that are currently available to extensions, where exactly
45+
the filter is applied, and what data might be passed to the `CheckoutFilterFunction`.
46+
47+
### Cart Line Items
48+
Line items refers to each item listed in the cart or checkout. For instance
49+
the "Sunglasses" and "Beanie with logo" in this image are the line items.
50+
51+
<img src="https://user-images.githubusercontent.com/5656702/117027554-b7c3eb00-acf4-11eb-8af1-b8bedbe20e05.png" width=600 />
52+
53+
The following filters are available for line items:
54+
55+
| Filter name | Description | Return type |
56+
|---|---|---|
57+
| `itemName` | Used to change the name of the item before it is rendered onto the page | `string`
58+
| `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.
59+
| `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.
60+
| `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.
61+
62+
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))
63+
64+
### Order Summary Items
65+
In the Checkout block, there is a sidebar that contains a summary of what the customer is about to purchase.
66+
There are some filters available to modify the way certain elements are displayed on each item.
67+
68+
The sale badges are not shown here, so those filters are not applied in the Order Summary.
69+
70+
<img src="https://user-images.githubusercontent.com/5656702/117026942-1b014d80-acf4-11eb-8515-b9b777d96a74.png" width=400 />
71+
72+
| Filter name | Description | Return type |
73+
|---|---|---|
74+
| `itemName` | Used to change the name of the item before it is rendered onto the page | `string`
75+
| `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.
76+
| `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.
77+
78+
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))
79+
80+
### Totals footer item (in Cart and Checkout)
81+
82+
The word 'Total' that precedes the amount due, present in both the Cart _and_ Checkout blocks, is also passed through filters.
83+
84+
| Filter name | Description | Return type |
85+
|---|---|---|
86+
| `totalLabel` | This is the label for the cart total. It defaults to 'Total' (or the word for 'Total' if using translations). | `string`
87+
88+
There are no additional arguments passed to this filter.
89+
90+
## Examples
91+
92+
### Changing the wording of the Totals label in the Cart and Checkout
93+
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.
94+
95+
To make it easier to understand what the customer is paying and why, let's change the value of `Total` to `Deposit due today`.
96+
97+
1. We need to create a `CheckoutFilterFunction`.
98+
```typescript
99+
const replaceTotalWithDeposit = () => 'Deposit due today';
100+
```
101+
2. Now we need to register this filter function, and have it executed when the `totalLabel` filter is applied.
102+
We can access the `__experimentalRegisterCheckoutFilters` function on the `window.wc.blocksCheckout` object.
103+
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.
104+
```typescript
105+
const { __experimentalRegisterCheckoutFilters } = window.wc.blocksCheckout;
106+
__experimentalRegisterCheckoutFilters( 'my-hypothetical-deposit-plugin', {
107+
totalLabel: replaceTotalWithDeposit
108+
} );
109+
```
110+
111+
| Before | After |
112+
|---|---|
113+
| <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 /> |
114+
115+
116+
### Changing the format of the item's single price
117+
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
118+
that's the price per item.
119+
120+
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
121+
Cart context, our function will need to check the additional arguments passed to it to ensure the `context` value is `cart`.
122+
123+
We can see from the table above, that our function needs to return a string that contains a substring of `<price/>`.
124+
This is a placeholder for the numeric value. The Cart block will interpolate the value into the string we return.
125+
```typescript
126+
const appendTextToPriceInCart = ( value, extensions, args ) => {
127+
if( args?.context !== 'cart') {
128+
// Return early since this filter is not being applied in the Cart context.
129+
// We must return the original value we received here.
130+
return value;
131+
}
132+
return '<price/> per item';
133+
};
134+
```
135+
2. Now we must register it. Refer to the first example for information about `__experimentalRegisterCheckoutFilters`.
136+
```typescript
137+
const { __experimentalRegisterCheckoutFilters } = window.wc.blocksCheckout;
138+
__experimentalRegisterCheckoutFilters( 'my-hypothetical-price-plugin', {
139+
subtotalPriceFormat: appendTextToPriceInCart
140+
} );
141+
```
142+
143+
144+
| Before | After |
145+
|---|---|
146+
| <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 /> |
147+
148+
## Troubleshooting
149+
If you are logged in to the store as an administrator, you should be shown an error like this if your filter is not
150+
working correctly.
151+
152+
<img src="https://user-images.githubusercontent.com/5656702/117035848-b4ccf880-acfc-11eb-870a-31ae86dd6496.png" width=600 />
153+
154+
The error will also be shown in your console.

packages/checkout/registry/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { CURRENT_USER_IS_ADMIN } from '@woocommerce/settings';
1111
import { returnTrue } from '../utils';
1212

1313
type CheckoutFilterFunction = < T >(
14-
label: T,
14+
value: T,
1515
extensions: Record< string, unknown >,
1616
args?: CheckoutFilterArguments
1717
) => T;

0 commit comments

Comments
 (0)