Merge branch 'trunk' into 30050

This commit is contained in:
Néstor Soriano 2024-09-11 09:32:51 +02:00 committed by GitHub
commit 0217b4e061
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
439 changed files with 7206 additions and 12661 deletions

View File

@ -17,9 +17,9 @@ when:
- woo-fse - woo-fse
- author: - author:
teamIs: teamIs:
- vortex - flux
ignore: ignore:
nameIs: nameIs:
assign: assign:
teams: teams:
- vortex - flux

34
.github/workflows/storybook-pages.yml vendored Normal file
View File

@ -0,0 +1,34 @@
name: Storybook GitHub Pages
on:
workflow_dispatch:
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
if: ${{ github.repository == 'woocommerce/woocommerce' }}
steps:
- name: Checkout
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
ref: trunk
fetch-depth: 0
- name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo
with:
install: true
- name: Build Storybook
run: pnpm --filter='@woocommerce/storybook' build-storybook --quiet
- name: Deploy
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./tools/storybook/storybook-static
force_orphan: true

View File

@ -59,7 +59,7 @@ For our Woo extension, we'll be appending our field right at the end with `wooco
Let's get started with creating a new class which will hold the code for the field. Add a new file with the name `class-product-fields.php` to the `/includes/admin/` folder. Within the class, we add our namespace, an abort if anyone tries to call the file directly and a \_\_construct method which calls the `hooks()` method: Let's get started with creating a new class which will hold the code for the field. Add a new file with the name `class-product-fields.php` to the `/includes/admin/` folder. Within the class, we add our namespace, an abort if anyone tries to call the file directly and a \_\_construct method which calls the `hooks()` method:
```php ```php
<?php &lt;?php
namespace WooProductField\Admin; namespace WooProductField\Admin;
@ -68,7 +68,7 @@ defined( 'ABSPATH' ) || exit;
class ProductFields { class ProductFields {
public function __construct() { public function __construct() {
$this->hooks(); $this-&gt;hooks();
} }
private function hooks() {} private function hooks() {}
@ -93,19 +93,19 @@ With the class set up and being called, we can create a function to add the cust
```php ```php
public function add_field() { public function add_field() {
global $product_object; global $product_object;
?> ?&gt;
<div class="inventory_new_stock_information options_group show_if_simple show_if_variable"> &lt;div class="inventory_new_stock_information options_group show_if_simple show_if_variable"&gt;
<?php woocommerce_wp_text_input( &lt;?php woocommerce_wp_text_input(
array( array(
'id' => '_new_stock_information', 'id' =&gt; '_new_stock_information',
'label' => __( 'New Stock', 'woo_product_field' ), 'label' =&gt; __( 'New Stock', 'woo_product_field' ),
'description' => __( 'Information shown in store', 'woo_product_field' ), 'description' =&gt; __( 'Information shown in store', 'woo_product_field' ),
'desc_tip' => true, 'desc_tip' =&gt; true,
'value' => $product_object->get_meta( '_new_stock_information' ) 'value' =&gt; $product_object-&gt;get_meta( '_new_stock_information' )
) )
); ?> ); ?&gt;
</div> &lt;/div&gt;
<?php &lt;?php
} }
``` ```
@ -119,8 +119,8 @@ Now that we have our field, we need to save it. For this, we can hook into wooco
public function save_field( $post_id, $post ) { public function save_field( $post_id, $post ) {
if ( isset( $_POST['_new_stock_information'] ) ) { if ( isset( $_POST['_new_stock_information'] ) ) {
$product = wc_get_product( intval( $post_id ) ); $product = wc_get_product( intval( $post_id ) );
$product->update_meta_data( '_new_stock_information', sanitize_text_field( $_POST['_new_stock_information'] ) ); $product-&gt;update_meta_data( '_new_stock_information', sanitize_text_field( $_POST['_new_stock_information'] ) );
$product->save_meta_data(); $product-&gt;save_meta_data();
} }
} }
``` ```
@ -142,12 +142,12 @@ If we add data and save the product, then the new meta data is inserted into the
At this point you have a working extension that saves a custom field for a product as product meta. At this point you have a working extension that saves a custom field for a product as product meta.
Showing the field in the store Showing the field in the store
If we want to display the new field in our store, then we can do this with the `get_meta()` method of the Woo product class: `$product->get_meta( '\_new_stock_information' )` If we want to display the new field in our store, then we can do this with the `get_meta()` method of the Woo product class: `$product-&gt;get_meta( '\_new_stock_information' )`
Let's get started by creating a new file /includes/class-product.php. You may have noticed that this is outside the `/admin/` folder as this code will run in the front. So when we set up the class, we also adjust the namespace accordingly: Let's get started by creating a new file /includes/class-product.php. You may have noticed that this is outside the `/admin/` folder as this code will run in the front. So when we set up the class, we also adjust the namespace accordingly:
```php ```php
<?php &lt;?php
namespace WooProductField; namespace WooProductField;
@ -155,7 +155,7 @@ defined( 'ABSPATH' ) || exit;
class Product { class Product {
public function __construct() { public function __construct() {
$this->hooks(); $this-&gt;hooks();
} }
private function hooks() { } private function hooks() { }
@ -190,9 +190,9 @@ In our function we output the stock information with the [appropriate escape fun
```php ```php
public function add_stock_info() { public function add_stock_info() {
global $product; global $product;
?> ?&gt;
<p><?php echo esc_html( $product->get_meta( '_new_stock_information' ) ); ?> </p> &lt;p&gt;&lt;?php echo esc_html( $product-&gt;get_meta( '_new_stock_information' ) ); ?&gt; &lt;/p&gt;
<?php &lt;?php
} }
``` ```
@ -223,14 +223,14 @@ The setup is very similar to simple products, the main difference is that we nee
```php ```php
public function add_variation_field( $loop, $variation_data, $variation ) { public function add_variation_field( $loop, $variation_data, $variation ) {
$variation_product = wc_get_product( $variation->ID ); $variation_product = wc_get_product( $variation-&gt;ID );
woocommerce_wp_text_input( woocommerce_wp_text_input(
array( array(
'id' => '\_new_stock_information' . '[' . $loop . ']', 'id' =&gt; '\_new_stock_information' . '[' . $loop . ']',
'label' => \_\_( 'New Stock Information', 'woo_product_field' ), 'label' =&gt; \_\_( 'New Stock Information', 'woo_product_field' ),
'wrapper_class' => 'form-row form-row-full', 'wrapper_class' =&gt; 'form-row form-row-full',
'value' => $variation_product->get_meta( '\_new_stock_information' ) 'value' =&gt; $variation_product-&gt;get_meta( '\_new_stock_information' )
) )
); );
} }
@ -242,8 +242,8 @@ For saving we use:
public function save_variation_field( $variation_id, $i ) { public function save_variation_field( $variation_id, $i ) {
if ( isset( $_POST['_new_stock_information'][$i] ) ) { if ( isset( $_POST['_new_stock_information'][$i] ) ) {
$variation_product = wc_get_product( $variation_id ); $variation_product = wc_get_product( $variation_id );
$variation_product->update_meta_data( '_new_stock_information', sanitize_text_field( $_POST['_new_stock_information'][$i] ) ); $variation_product-&gt;update_meta_data( '_new_stock_information', sanitize_text_field( $_POST['_new_stock_information'][$i] ) );
$variation_product->save_meta_data(); $variation_product-&gt;save_meta_data();
} }
} }
``` ```

View File

@ -1109,16 +1109,16 @@ The contents of this block will display when there are no products found.
- **Supports:** align, color (background, gradients, link, text), typography (fontSize, lineHeight), ~~html~~, ~~reusable~~ - **Supports:** align, color (background, gradients, link, text), typography (fontSize, lineHeight), ~~html~~, ~~reusable~~
- **Attributes:** - **Attributes:**
## Product Filter (Experimental) - woocommerce/product-filter ## Product Filters (Experimental) - woocommerce/product-filters
A block that adds product filters to the product collection. Let shoppers filter products displayed on the page.
- **Name:** woocommerce/product-filter - **Name:** woocommerce/product-filters
- **Category:** woocommerce - **Category:** woocommerce
- **Ancestor:** woocommerce/product-filters - **Ancestor:**
- **Parent:** - **Parent:**
- **Supports:** ~~html~~, ~~inserter~~, ~~reusable~~ - **Supports:** align, color (background, text), interactivity, layout (allowJustification, allowOrientation, allowVerticalAlignment, default, ~~allowInheriting~~), spacing (blockGap), typography (fontSize, textAlign), ~~inserter~~, ~~multiple~~
- **Attributes:** attributeId, filterType, heading, isPreview - **Attributes:** overlay, overlayButtonStyle, overlayIcon, overlayIconSize
## Filter Options - woocommerce/product-filter-active ## Filter Options - woocommerce/product-filter-active
@ -1153,50 +1153,6 @@ Allows shoppers to reset this filter.
- **Supports:** interactivity, ~~inserter~~ - **Supports:** interactivity, ~~inserter~~
- **Attributes:** - **Attributes:**
## Filter Options - woocommerce/product-filter-price
Enable customers to filter the product collection by choosing a price range.
- **Name:** woocommerce/product-filter-price
- **Category:** woocommerce
- **Ancestor:** woocommerce/product-filter
- **Parent:**
- **Supports:** interactivity, ~~inserter~~
- **Attributes:** inlineInput, showInputFields
## Filter Options - woocommerce/product-filter-rating
Enable customers to filter the product collection by rating.
- **Name:** woocommerce/product-filter-rating
- **Category:** woocommerce
- **Ancestor:** woocommerce/product-filter
- **Parent:**
- **Supports:** color (text, ~~background~~), interactivity, ~~inserter~~
- **Attributes:** className, displayStyle, isPreview, selectType, showCounts
## Filter Options - woocommerce/product-filter-stock-status
Enable customers to filter the product collection by stock status.
- **Name:** woocommerce/product-filter-stock-status
- **Category:** woocommerce
- **Ancestor:** woocommerce/product-filter
- **Parent:**
- **Supports:** color (text, ~~background~~), interactivity, ~~html~~, ~~inserter~~, ~~multiple~~
- **Attributes:** className, displayStyle, isPreview, selectType, showCounts
## Product Filters (Experimental) - woocommerce/product-filters
Let shoppers filter products displayed on the page.
- **Name:** woocommerce/product-filters
- **Category:** woocommerce
- **Ancestor:**
- **Parent:**
- **Supports:** align, color (background, text), interactivity, layout (allowJustification, allowOrientation, allowVerticalAlignment, default, ~~allowInheriting~~), spacing (blockGap), typography (fontSize, textAlign), ~~inserter~~, ~~multiple~~
- **Attributes:** overlay, overlayButtonStyle, overlayIcon, overlayIconSize
## Product Filters Overlay (Experimental) - woocommerce/product-filters-overlay ## Product Filters Overlay (Experimental) - woocommerce/product-filters-overlay
Display product filters in an overlay on top of a page. Display product filters in an overlay on top of a page.
@ -1219,6 +1175,50 @@ Display overlay navigation controls.
- **Supports:** align (center, left, right), color (background, text), layout (default, ~~allowEditing~~), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~inserter~~ - **Supports:** align (center, left, right), color (background, text), layout (default, ~~allowEditing~~), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~inserter~~
- **Attributes:** align, buttonStyle, iconSize, navigationStyle, overlayMode, style, triggerType - **Attributes:** align, buttonStyle, iconSize, navigationStyle, overlayMode, style, triggerType
## Filter Options - woocommerce/product-filter-price
Enable customers to filter the product collection by choosing a price range.
- **Name:** woocommerce/product-filter-price
- **Category:** woocommerce
- **Ancestor:** woocommerce/product-filter
- **Parent:**
- **Supports:** interactivity, ~~inserter~~
- **Attributes:** inlineInput, showInputFields
## Product Filter (Experimental) - woocommerce/product-filter
A block that adds product filters to the product collection.
- **Name:** woocommerce/product-filter
- **Category:** woocommerce
- **Ancestor:** woocommerce/product-filters
- **Parent:**
- **Supports:** inserter, ~~html~~, ~~reusable~~
- **Attributes:** attributeId, filterType, heading, isPreview
## Filter Options - woocommerce/product-filter-rating
Enable customers to filter the product collection by rating.
- **Name:** woocommerce/product-filter-rating
- **Category:** woocommerce
- **Ancestor:** woocommerce/product-filter
- **Parent:**
- **Supports:** color (text, ~~background~~), interactivity, ~~inserter~~
- **Attributes:** className, displayStyle, isPreview, selectType, showCounts
## Filter Options - woocommerce/product-filter-stock-status
Enable customers to filter the product collection by stock status.
- **Name:** woocommerce/product-filter-stock-status
- **Category:** woocommerce
- **Ancestor:** woocommerce/product-filter
- **Parent:**
- **Supports:** color (text, ~~background~~), interactivity, ~~html~~, ~~inserter~~
- **Attributes:** className, displayStyle, isPreview, selectType, showCounts
## Product Gallery (Beta) - woocommerce/product-gallery ## Product Gallery (Beta) - woocommerce/product-gallery
Showcase your products relevant images and media. Showcase your products relevant images and media.

View File

@ -14,17 +14,17 @@ if ( ! function_exists( 'YOUR_PREFIX_login_message' ) ) {
*/ */
function YOUR_PREFIX_login_message() { function YOUR_PREFIX_login_message() {
if ( get_option( 'woocommerce_enable_myaccount_registration' ) == 'yes' ) { if ( get_option( 'woocommerce_enable_myaccount_registration' ) == 'yes' ) {
?> ?&gt;
<div class="woocommerce-info"> &lt;div class="woocommerce-info"&gt;
<p><?php _e( 'Returning customers login. New users register for next time so you can:', 'YOUR-TEXTDOMAIN' ); ?></p> &lt;p&gt;&lt;?php _e( 'Returning customers login. New users register for next time so you can:', 'YOUR-TEXTDOMAIN' ); ?&gt;&lt;/p&gt;
<ul> &lt;ul&gt;
<li><?php _e( 'View your order history', 'YOUR-TEXTDOMAIN' ); ?></li> &lt;li&gt;&lt;?php _e( 'View your order history', 'YOUR-TEXTDOMAIN' ); ?&gt;&lt;/li&gt;
<li><?php _e( 'Check on your orders', 'YOUR-TEXTDOMAIN' ); ?></li> &lt;li&gt;&lt;?php _e( 'Check on your orders', 'YOUR-TEXTDOMAIN' ); ?&gt;&lt;/li&gt;
<li><?php _e( 'Edit your addresses', 'YOUR-TEXTDOMAIN' ); ?></li> &lt;li&gt;&lt;?php _e( 'Edit your addresses', 'YOUR-TEXTDOMAIN' ); ?&gt;&lt;/li&gt;
<li><?php _e( 'Change your password', 'YOUR-TEXTDOMAIN' ); ?></li> &lt;li&gt;&lt;?php _e( 'Change your password', 'YOUR-TEXTDOMAIN' ); ?&gt;&lt;/li&gt;
</ul> &lt;/ul&gt;
</div> &lt;/div&gt;
<?php &lt;?php
} }
} }
add_action( 'woocommerce_before_customer_login_form', 'YOUR_PREFIX_login_message' ); add_action( 'woocommerce_before_customer_login_form', 'YOUR_PREFIX_login_message' );

View File

@ -38,7 +38,7 @@ function big_apple_get_tax_class( $tax_class, $product ) {
Some merchants may require different tax rates to be applied based on a customer role to accommodate for wholesale status or tax exemption. Some merchants may require different tax rates to be applied based on a customer role to accommodate for wholesale status or tax exemption.
To enable this functionality, add the following snippet to your child theme's functions.php file or via a code snippet plugin. In this snippet, users with “administrator” capabilities will be assigned the **Zero rate tax class**. Adjust it according to your requirements. To enable this functionality, add the following snippet to your child theme's functions.php file or via a code snippet plugin. In this snippet, users with "administrator" capabilities will be assigned the **Zero rate tax class**. Adjust it according to your requirements.
```php ```php
<?php <?php

View File

@ -127,3 +127,9 @@ These instructions presume you're currently have your `docs/` branch open and yo
If you are a non-technical contributor who isn't experienced with command line tools, we're still happy to receive your contributions. If you're unable to include an updated manifest, please ensure that you mention this in your pull request's description. If you are a non-technical contributor who isn't experienced with command line tools, we're still happy to receive your contributions. If you're unable to include an updated manifest, please ensure that you mention this in your pull request's description.
If you're a technical contributor who is able to regenerate the manifest, we request that you do so where possible. If you're a technical contributor who is able to regenerate the manifest, we request that you do so where possible.
## Caveats
* Emojis are not supported.
* Avoid copy pasting content from editors such as Google docs. For example, quotation-mark characters in these editors may not translate properly when ingested by our plugin.
* You may want to references HTML like content, however, our plugin is set up to strip non-allowlisted HTML elements. You may use an [HTML named references](https://developer.mozilla.org/en-US/docs/Glossary/Character_reference) to create an HTML like tag, by using the less-than (<) and greater-than (>) symbol named references.

View File

@ -74,7 +74,7 @@
"post_title": "Blocks reference", "post_title": "Blocks reference",
"menu_title": "Blocks Reference", "menu_title": "Blocks Reference",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/building-a-woo-store/block-references.md", "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/building-a-woo-store/block-references.md",
"hash": "329f17097ce67074a915d7814b2363e8b9e908910c1f7b196c8f4fd8594cc55c", "hash": "9bbd3555641a70a0d7c24c818323a9270e437a6446998de9a6506e0c2ed6ddf5",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/building-a-woo-store/block-references.md", "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/building-a-woo-store/block-references.md",
"id": "1fbe91d7fa4fafaf35f0297e4cee1e7958756aed" "id": "1fbe91d7fa4fafaf35f0297e4cee1e7958756aed"
}, },
@ -83,7 +83,7 @@
"menu_title": "Add Custom Fields to Products", "menu_title": "Add Custom Fields to Products",
"tags": "how-to", "tags": "how-to",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/building-a-woo-store/adding-a-custom-field-to-variable-products.md", "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/building-a-woo-store/adding-a-custom-field-to-variable-products.md",
"hash": "59ef97ed2053dedfa5e7c658d5c7fa470d8110afc9b84584cb32b58389bf5687", "hash": "fe8cf43940f5166bf69f102aa4643cbe32415b1167d6b6d8968d434a4d113879",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/building-a-woo-store/adding-a-custom-field-to-variable-products.md", "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/building-a-woo-store/adding-a-custom-field-to-variable-products.md",
"id": "64b686dcd5fdd4842be2fc570108231d5a8bfc1b" "id": "64b686dcd5fdd4842be2fc570108231d5a8bfc1b"
} }
@ -414,7 +414,7 @@
"menu_title": "Configuring special tax scenarios", "menu_title": "Configuring special tax scenarios",
"tags": "code-snippet, tax", "tags": "code-snippet, tax",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/code-snippets/configuring_special_tax_scenarios.md", "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/code-snippets/configuring_special_tax_scenarios.md",
"hash": "acce5111eb917b7381d9bdcb260c72870e89d723195b86522050032741c5107c", "hash": "c2459568db70b97d9a901c83eecb7737746499fe03cc84133aab3cf981b5c96a",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/code-snippets/configuring_special_tax_scenarios.md", "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/code-snippets/configuring_special_tax_scenarios.md",
"id": "a8ab8b6734ba2ac5af7c6653635d15548abdab2a" "id": "a8ab8b6734ba2ac5af7c6653635d15548abdab2a"
}, },
@ -439,7 +439,7 @@
"post_title": "Add a message above the login / register form", "post_title": "Add a message above the login / register form",
"tags": "code-snippet", "tags": "code-snippet",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/code-snippets/before-login--register-form.md", "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/code-snippets/before-login--register-form.md",
"hash": "49f0d942364ea6815e799972894406cb0963164adfb6c373222dcf7fb0ee6fb9", "hash": "dd3d61cd1f99d459956167041718e6a25693901a810d4bde47613d5ea2595ca3",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/code-snippets/before-login--register-form.md", "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/code-snippets/before-login--register-form.md",
"id": "26ea2036f16952c8c965f2ab38ab214e421aa615" "id": "26ea2036f16952c8c965f2ab38ab214e421aa615"
}, },
@ -594,7 +594,7 @@
"post_title": "Contributing Technical Documentation", "post_title": "Contributing Technical Documentation",
"menu_title": "Contributing Docs", "menu_title": "Contributing Docs",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/contributing-docs/contributing-docs.md", "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/contributing-docs/contributing-docs.md",
"hash": "ee2eed4bc33ccbc4a84a2c73ee503f2df5a92183013e3f703bb439edab0a3fe3", "hash": "6733ba23f82a6e784ef10e2862aa3afd8a61e32181e157f67ccabfc8354aa989",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/contributing-docs/contributing-docs.md", "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/contributing-docs/contributing-docs.md",
"id": "71c1a72bfd4d5ae6aa656d4264b1bf3beb6eca1c" "id": "71c1a72bfd4d5ae6aa656d4264b1bf3beb6eca1c"
} }
@ -691,7 +691,7 @@
"post_title": "Integrating with coming soon mode", "post_title": "Integrating with coming soon mode",
"tags": "how-to, coming-soon", "tags": "how-to, coming-soon",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/extension-development/integrating-coming-soon-mode.md", "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/extension-development/integrating-coming-soon-mode.md",
"hash": "910dbbab77c6fb4735e7704796242c38d86f3bf3b897de1075338eb47194f8f5", "hash": "8c2087952ae79bb4c3e3977c57d9e933fcfaa418a5bc643b3827059daa5879a7",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/extension-development/integrating-coming-soon-mode.md", "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/extension-development/integrating-coming-soon-mode.md",
"id": "787743efb6ef0ad509b17735eaf58b2a9a08afbc" "id": "787743efb6ef0ad509b17735eaf58b2a9a08afbc"
}, },
@ -727,7 +727,7 @@
"menu_title": "Implement merchant onboarding", "menu_title": "Implement merchant onboarding",
"tags": "how-to", "tags": "how-to",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/extension-development/handling-merchant-onboarding.md", "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/extension-development/handling-merchant-onboarding.md",
"hash": "a34e2c637ac47adb1dd2e7b2510fb3eb7ff517b8f31e9cfa6fe4d3920f2567e7", "hash": "c73e3c5015e6cda3be9ebd2d5fbda590ac9fa599e5fb02163c971c01060970ad",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/extension-development/handling-merchant-onboarding.md", "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/extension-development/handling-merchant-onboarding.md",
"id": "89fe15dc232379f546852822230c334d3d940b93" "id": "89fe15dc232379f546852822230c334d3d940b93"
}, },
@ -939,7 +939,7 @@
"menu_title": "Enable HPOS for large stores", "menu_title": "Enable HPOS for large stores",
"tags": "how-to", "tags": "how-to",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/high-performance-order-storage/guide-large-store.md", "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/high-performance-order-storage/guide-large-store.md",
"hash": "8bcae74d27e3a4ee9a902719c7e8d5aec4a4d82d7c14acd8665a72b9d4758181", "hash": "1e144ac0d0a7c869093533bf94a1f218a42930a3f3edcbcfdd1210448243a992",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/high-performance-order-storage/guide-large-store.md", "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/high-performance-order-storage/guide-large-store.md",
"id": "b6156ac7b77d75022867e9ebb968bc9c1c35f0da" "id": "b6156ac7b77d75022867e9ebb968bc9c1c35f0da"
}, },
@ -1033,7 +1033,7 @@
"menu_title": "Performance best practices", "menu_title": "Performance best practices",
"tags": "reference", "tags": "reference",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/performance/performance-best-practices.md", "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/performance/performance-best-practices.md",
"hash": "5af1f4e4085e85a1693390f40e238cbd6a4a0b7d5d304afdda935c34fed97c64", "hash": "3c49b5553e99b64ecdbdad565866479f7af5e474dbbcc9aa36d711c02d8b0906",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/performance/performance-best-practices.md", "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/performance/performance-best-practices.md",
"id": "35bda1cd7068d6179a9e46cca8d7dc2694d0df96" "id": "35bda1cd7068d6179a9e46cca8d7dc2694d0df96"
} }
@ -1050,7 +1050,7 @@
"menu_title": "Registering custom collections", "menu_title": "Registering custom collections",
"tags": "how-to", "tags": "how-to",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/product-collection-block/register-product-collection.md", "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/product-collection-block/register-product-collection.md",
"hash": "e3df65c5eec52e4bb797e34c040dbb8f820ea6571e9ce50b1d518e95ca6cb169", "hash": "27c321bed35524d74019e015f5eed6cdca7e6c2efe0bc89ffdd2b9b5d43c47e8",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/product-collection-block/register-product-collection.md", "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/product-collection-block/register-product-collection.md",
"id": "3bf26fc7c56ae6e6a56e1171f750f5204fcfcece" "id": "3bf26fc7c56ae6e6a56e1171f750f5204fcfcece"
}, },
@ -1059,7 +1059,7 @@
"menu_title": "DOM Events", "menu_title": "DOM Events",
"tags": "how-to", "tags": "how-to",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/product-collection-block/dom-events.md", "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/product-collection-block/dom-events.md",
"hash": "fbad20bc55cc569161e80478c0789db3c34cf35513e669554af36db1de967a26", "hash": "59a4b49eb146774d33229bc60ab7d8f74381493f6e7089ca8f0e2d0eb433a7a4",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/product-collection-block/dom-events.md", "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/product-collection-block/dom-events.md",
"id": "c8d247b91472740075871e6b57a9583d893ac650" "id": "c8d247b91472740075871e6b57a9583d893ac650"
} }
@ -1411,7 +1411,7 @@
"categories": [] "categories": []
}, },
{ {
"content": "\nProperly setting up your test environment and writing tests when contributing to WooCommrece core are essential parts of our development pipeline. The links below are also included in our [Contributing Guidelines](https://github.com/woocommerce/woocommerce/blob/trunk/.github/CONTRIBUTING.md) on GitHub.\n\nIf you have any questions about testing please reach out to the developer community in our public channels([Developer Blog](https://developer.woocommerce.com/blog/), [GitHub Discussions](https://github.com/woocommerce/woocommerce/discussions), or [Community Slack](https://woocommerce.com/community-slack/)).\n\n## Unit Testing\n\n[End-to-end tests](https://github.com/woocommerce/woocommerce/tree/trunk/plugins/woocommerce/tests/e2e-pw) are powered by `Playwright`. The test site is spun up using `wp-env` ([recommended](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/)), but we will continue to support `e2e-environment` in the meantime, and slowly [deprecate](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/tests/e2e/README.md) `Puppeteer` testing. \n\n## API Testing\n\n`api-core-tests` is a [package](https://github.com/woocommerce/woocommerce/tree/trunk/plugins/woocommerce/tests/api-core-tests#guide-for-writing-api-tests) that contains automated API tests for WooCommerce, based on `Playwright` and `wp-env`. It supersedes the SuperTest based `api-core-tests` package and `e2e-environment` setup, which we will gradually deprecate.\n\n## Calls for Testing\n\nKeep tabs on calls for testing on our developer blog, and make sure to read our beta testing instructions to help us build new features and enhancements.\n", "content": "\nProperly setting up your test environment and writing tests when contributing to WooCommerce core are essential parts of our development pipeline. The links below are also included in our [Contributing Guidelines](https://github.com/woocommerce/woocommerce/blob/trunk/.github/CONTRIBUTING.md) on GitHub.\n\nIf you have any questions about testing please reach out to the developer community in our public channels([Developer Blog](https://developer.woocommerce.com/blog/), [GitHub Discussions](https://github.com/woocommerce/woocommerce/discussions), or [Community Slack](https://woocommerce.com/community-slack/)).\n\n## Unit Testing\n\n[End-to-end tests](https://github.com/woocommerce/woocommerce/tree/trunk/plugins/woocommerce/tests/e2e-pw) are powered by `Playwright`. The test site is spun up using `wp-env` ([recommended](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/)), but we will continue to support `e2e-environment` in the meantime, and slowly [deprecate](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/tests/e2e/README.md) `Puppeteer` testing. \n\n## API Testing\n\n`api-core-tests` is a [package](https://github.com/woocommerce/woocommerce/tree/trunk/plugins/woocommerce/tests/api-core-tests#guide-for-writing-api-tests) that contains automated API tests for WooCommerce, based on `Playwright` and `wp-env`. It supersedes the SuperTest based `api-core-tests` package and `e2e-environment` setup, which we will gradually deprecate.\n\n## Calls for Testing\n\nKeep tabs on calls for testing on our developer blog, and make sure to read our beta testing instructions to help us build new features and enhancements.\n",
"category_slug": "testing", "category_slug": "testing",
"category_title": "Testing", "category_title": "Testing",
"posts": [ "posts": [
@ -1451,7 +1451,7 @@
{ {
"post_title": "Template structure & Overriding templates via a theme", "post_title": "Template structure & Overriding templates via a theme",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/theme-development/template-structure.md", "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/theme-development/template-structure.md",
"hash": "ff781eff7998ea93723f644bddd4f6da6f73c635bcfc3cd46950f03a8b83b26c", "hash": "a82d885c8395385dc51c976b2b51eec88cdcce72bf905fda78f97d77533ce079",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/theme-development/template-structure.md", "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/theme-development/template-structure.md",
"id": "34bfebec9fc45e680976814928a7b8a1778af14e" "id": "34bfebec9fc45e680976814928a7b8a1778af14e"
}, },
@ -1554,7 +1554,7 @@
"post_title": "WooCommerce Extension Guidelines - Navigation", "post_title": "WooCommerce Extension Guidelines - Navigation",
"menu_title": "Navigation", "menu_title": "Navigation",
"edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/ux-guidelines-extensions/navigation.md", "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/ux-guidelines-extensions/navigation.md",
"hash": "f14cbd3750451934d173ec43aa996067699bdd8fc05f5c34097d1967a79145db", "hash": "9c5313a31ebe26909a2cfaaa0bcfcdc9d5a0855ac0ddb9808832524be313ebb6",
"url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/ux-guidelines-extensions/navigation.md", "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/ux-guidelines-extensions/navigation.md",
"id": "9922abbcea787a91b3360f49fa53d5402a604e6b" "id": "9922abbcea787a91b3360f49fa53d5402a604e6b"
}, },
@ -1804,5 +1804,5 @@
"categories": [] "categories": []
} }
], ],
"hash": "2ecf48b6181dae0526b3858df889bce4e6ee425e10f2c43d151771d845c5a948" "hash": "8199f0d3c854474839300ed606f03f9f286ace35f65d7c47ffc6477762eaf51e"
} }

View File

@ -26,7 +26,7 @@ Setup tasks appear on the WooCommerce Admin home screen and prompt a merchant to
To register your task as an extended task list item, you'll need to start by creating a new PHP class that extends the Task class. This class will define the properties and behavior of your custom task. To register your task as an extended task list item, you'll need to start by creating a new PHP class that extends the Task class. This class will define the properties and behavior of your custom task.
```php ```php
<?php &lt;?php
/** /**
* Custom task example. * Custom task example.
* *
@ -113,37 +113,37 @@ import {
} from '@woocommerce/onboarding'; } from '@woocommerce/onboarding';
import { registerPlugin } from '@wordpress/plugins'; import { registerPlugin } from '@wordpress/plugins';
const Task = ( { onComplete, task, query } ) => { const Task = ( { onComplete, task, query } ) =&gt; {
// Implement your task UI/feature here. // Implement your task UI/feature here.
return <div></div>; return &lt;div&gt;&lt;/div&gt;;
}; };
registerPlugin( 'add-task-content', { registerPlugin( 'add-task-content', {
render: () => ( render: () =&gt; (
<WooOnboardingTask id="my-task"> &lt;WooOnboardingTask id="my-task"&gt;
{ ( { onComplete, query, task } ) => ( { ( { onComplete, query, task } ) =&gt; (
<Task onComplete={ onComplete } task={ task } query={ query } /> &lt;Task onComplete={ onComplete } task={ task } query={ query } /&gt;
) } ) }
</WooOnboardingTask> &lt;/WooOnboardingTask&gt;
), ),
} ); } );
registerPlugin( 'add-task-list-item', { registerPlugin( 'add-task-list-item', {
scope: 'woocommerce-tasks', scope: 'woocommerce-tasks',
render: () => ( render: () =&gt; (
<WooOnboardingTaskListItem id="my-task"> &lt;WooOnboardingTaskListItem id="my-task"&gt;
{ ( { defaultTaskItem: DefaultTaskItem } ) => ( { ( { defaultTaskItem: DefaultTaskItem } ) =&gt; (
// Add a custom wrapper around the default task item. // Add a custom wrapper around the default task item.
<div &lt;div
className="woocommerce-custom-tasklist-item" className="woocommerce-custom-tasklist-item"
style={ { style={ {
border: '1px solid red', border: '1px solid red',
} } } }
> &gt;
<DefaultTaskItem /> &lt;DefaultTaskItem /&gt;
</div> &lt;/div&gt;
) } ) }
</WooOnboardingTaskListItem> &lt;/WooOnboardingTaskListItem&gt;
), ),
} ); } );
``` ```
@ -168,37 +168,37 @@ import { registerPlugin } from '@wordpress/plugins';
Next, we create a [functional component](https://reactjs.org/docs/components-and-props.html) that returns our task card. The intermixed JavaScript/HTML syntax we're using here is called JSX. If you're unfamiliar with it, you can [read more about it in the React docs](https://reactjs.org/docs/introducing-jsx.html). Next, we create a [functional component](https://reactjs.org/docs/components-and-props.html) that returns our task card. The intermixed JavaScript/HTML syntax we're using here is called JSX. If you're unfamiliar with it, you can [read more about it in the React docs](https://reactjs.org/docs/introducing-jsx.html).
```js ```js
const Task = ( { onComplete, task } ) => { const Task = ( { onComplete, task } ) =&gt; {
const { actionTask } = useDispatch( ONBOARDING_STORE_NAME ); const { actionTask } = useDispatch( ONBOARDING_STORE_NAME );
const { isActioned } = task; const { isActioned } = task;
return ( return (
<Card className="woocommerce-task-card"> &lt;Card className="woocommerce-task-card"&gt;
<CardBody> &lt;CardBody&gt;
{ __( { __(
"This task's completion status is dependent on being actioned. The action button below will action this task, while the complete button will optimistically complete the task in the task list and redirect back to the task list. Note that in this example, the task must be actioned for completion to persist.", "This task's completion status is dependent on being actioned. The action button below will action this task, while the complete button will optimistically complete the task in the task list and redirect back to the task list. Note that in this example, the task must be actioned for completion to persist.",
'plugin-domain' 'plugin-domain'
) }{ ' ' } ) }{ ' ' }
<br /> &lt;br /&gt;
<br /> &lt;br /&gt;
{ __( 'Task actioned status: ', 'plugin-domain' ) }{ ' ' } { __( 'Task actioned status: ', 'plugin-domain' ) }{ ' ' }
{ isActioned ? 'actioned' : 'not actioned' } { isActioned ? 'actioned' : 'not actioned' }
<br /> &lt;br /&gt;
<br /> &lt;br /&gt;
<div> &lt;div&gt;
<button &lt;button
onClick={ () => { onClick={ () =&gt; {
actionTask( 'my-task' ); actionTask( 'my-task' );
} } } }
> &gt;
{ __( 'Action task', 'plugin-domain' ) } { __( 'Action task', 'plugin-domain' ) }
</button> &lt;/button&gt;
<button onClick={ onComplete }> &lt;button onClick={ onComplete }&gt;
{ __( 'Complete', 'plugin-domain' ) } { __( 'Complete', 'plugin-domain' ) }
</button> &lt;/button&gt;
</div> &lt;/div&gt;
</CardBody> &lt;/CardBody&gt;
</Card> &lt;/Card&gt;
); );
}; };
``` ```
@ -211,14 +211,14 @@ Next, we register the Task component as a plugin named "add-task-content" using
```js ```js
registerPlugin( 'add-task-content', { registerPlugin( 'add-task-content', {
render: () => ( render: () =&gt; (
{ ( { { ( {
onComplete, onComplete,
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
query, query,
task, task,
} ) => } } ) =&gt; }
), ),
scope: 'woocommerce-tasks', scope: 'woocommerce-tasks',
@ -232,20 +232,20 @@ Finally, we register another plugin named "my-task-list-item-plugin." This plugi
```js ```js
registerPlugin( 'my-task-list-item-plugin', { registerPlugin( 'my-task-list-item-plugin', {
scope: 'woocommerce-tasks', scope: 'woocommerce-tasks',
render: () => ( render: () =&gt; (
<WooOnboardingTaskListItem id="my-task"> &lt;WooOnboardingTaskListItem id="my-task"&gt;
{ ( { defaultTaskItem: DefaultTaskItem } ) => ( { ( { defaultTaskItem: DefaultTaskItem } ) =&gt; (
// Add a custom wrapper around the default task item. // Add a custom wrapper around the default task item.
<div &lt;div
className="woocommerce-custom-tasklist-item" className="woocommerce-custom-tasklist-item"
style={ { style={ {
border: '1px solid red', border: '1px solid red',
} } } }
> &gt;
<DefaultTaskItem /> &lt;DefaultTaskItem /&gt;
</div> &lt;/div&gt;
) } ) }
</WooOnboardingTaskListItem> &lt;/WooOnboardingTaskListItem&gt;
), ),
} ); } );
``` ```
@ -344,7 +344,7 @@ import { addFilter } from '@wordpress/hooks';
addFilter( addFilter(
'woocommerce_admin_homescreen_quicklinks', 'woocommerce_admin_homescreen_quicklinks',
'my-extension', 'my-extension',
( quickLinks ) => { ( quickLinks ) =&gt; {
return [ return [
...quickLinks, ...quickLinks,
{ {
@ -374,7 +374,7 @@ Despite being a part of the new React-powered admin experience in WooCommerce, A
The recommended approach for using Admin Notes is to encapsulate your note within its own class that uses the [NoteTraits](https://github.com/woocommerce/woocommerce-admin/blob/831c9ff13a862f22cf53d3ae676daeabbefe90ad/src/Notes/NoteTraits.php) trait included with WooCommerce Admin. Below is a simple example of what this might look like: The recommended approach for using Admin Notes is to encapsulate your note within its own class that uses the [NoteTraits](https://github.com/woocommerce/woocommerce-admin/blob/831c9ff13a862f22cf53d3ae676daeabbefe90ad/src/Notes/NoteTraits.php) trait included with WooCommerce Admin. Below is a simple example of what this might look like:
```php ```php
<?php &lt;?php
/** /**
* Simple note provider * Simple note provider
* *
@ -423,10 +423,10 @@ class ExampleNote {
$note = new Automattic\WooCommerce\Admin\Notes\Note(); $note = new Automattic\WooCommerce\Admin\Notes\Note();
// Set our note's title. // Set our note's title.
$note->set_title( 'Getting Started' ); $note-&gt;set_title( 'Getting Started' );
// Set our note's content. // Set our note's content.
$note->set_content( $note-&gt;set_content(
sprintf( sprintf(
'Extension activated on %s.', $activated_time_formatted 'Extension activated on %s.', $activated_time_formatted
) )
@ -436,41 +436,41 @@ class ExampleNote {
// You can use this property to re-localize notes on the fly, but // You can use this property to re-localize notes on the fly, but
// that is just one use. You can store other data here too. This // that is just one use. You can store other data here too. This
// is backed by a longtext column in the database. // is backed by a longtext column in the database.
$note->set_content_data( (object) array( $note-&gt;set_content_data( (object) array(
'getting_started' => true, 'getting_started' =&gt; true,
'activated' => $activated_time, 'activated' =&gt; $activated_time,
'activated_formatted' => $activated_time_formatted 'activated_formatted' =&gt; $activated_time_formatted
) ); ) );
// Set the type of the note. Note types are defined as enum-style // Set the type of the note. Note types are defined as enum-style
// constants in the Note class. Available note types are: // constants in the Note class. Available note types are:
// error, warning, update, info, marketing. // error, warning, update, info, marketing.
$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL ); $note-&gt;set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
// Set the type of layout the note uses. Supported layout types are: // Set the type of layout the note uses. Supported layout types are:
// 'banner', 'plain', 'thumbnail' // 'banner', 'plain', 'thumbnail'
$note->set_layout( 'plain' ); $note-&gt;set_layout( 'plain' );
// Set the image for the note. This property renders as the src // Set the image for the note. This property renders as the src
// attribute for an img tag, so use a string here. // attribute for an img tag, so use a string here.
$note->set_image( '' ); $note-&gt;set_image( '' );
// Set the note name and source. You should store your extension's // Set the note name and source. You should store your extension's
// name (slug) in the source property of the note. You can use // name (slug) in the source property of the note. You can use
// the name property of the note to support multiple sub-types of // the name property of the note to support multiple sub-types of
// notes. This also gives you a handy way of namespacing your notes. // notes. This also gives you a handy way of namespacing your notes.
$note->set_source( 'inbox-note-example'); $note-&gt;set_source( 'inbox-note-example');
$note->set_name( self::NOTE_NAME ); $note-&gt;set_name( self::NOTE_NAME );
// Add action buttons to the note. A note can support 0, 1, or 2 actions. // Add action buttons to the note. A note can support 0, 1, or 2 actions.
// The first parameter is the action name, which can be used for event handling. // The first parameter is the action name, which can be used for event handling.
// The second parameter renders as the label for the button. // The second parameter renders as the label for the button.
// The third parameter is an optional URL for actions that require navigation. // The third parameter is an optional URL for actions that require navigation.
$note->add_action( $note-&gt;add_action(
'settings', 'Open Settings', '?page=wc-settings&tab=general' 'settings', 'Open Settings', '?page=wc-settings&tab=general'
); );
$note->add_action( $note-&gt;add_action(
'learn_more', 'Learn More', 'https://example.com' 'learn_more', 'Learn More', 'https://example.com'
); );
@ -559,12 +559,12 @@ Next, we'll instantiate a new `Note` object.
Once we have an instance of the Note class, we can work with its API to set its properties, starting with its title. Once we have an instance of the Note class, we can work with its API to set its properties, starting with its title.
`$note->set_title( 'Getting Started' );` `$note-&gt;set_title( 'Getting Started' );`
Then we'll use some of the timestamp data we collected above to set the note's content. Then we'll use some of the timestamp data we collected above to set the note's content.
```php ```php
$note->set_content( $note-&gt;set_content(
sprintf( sprintf(
'Extension activated on %s.', $activated_time_formatted 'Extension activated on %s.', $activated_time_formatted
) )
@ -574,41 +574,41 @@ $note->set_content(
In addition to regular content, notes also support structured content using the `content_data` property. You can use this property to re-localize notes on the fly, but that is just one use case. You can store other data here too. This is backed by a `longtext` column in the database. In addition to regular content, notes also support structured content using the `content_data` property. You can use this property to re-localize notes on the fly, but that is just one use case. You can store other data here too. This is backed by a `longtext` column in the database.
```php ```php
$note->set_content_data( (object) array( $note-&gt;set_content_data( (object) array(
'getting_started' => true, 'getting_started' =&gt; true,
'activated' => $activated_time, 'activated' =&gt; $activated_time,
'activated_formatted' => $activated_time_formatted 'activated_formatted' =&gt; $activated_time_formatted
) ); ) );
``` ```
Next, we'll set the note's `type` property. Note types are defined as enum-style class constants in the `Note` class. Available note types are _error_, _warning_, _update_, _info_, and _marketing_. When selecting a note type, be aware that the _error_ and _update_ result in the note being shown as a Store Alert, not in the Inbox. It's best to avoid using these types of notes unless you absolutely need to. Next, we'll set the note's `type` property. Note types are defined as enum-style class constants in the `Note` class. Available note types are _error_, _warning_, _update_, _info_, and _marketing_. When selecting a note type, be aware that the _error_ and _update_ result in the note being shown as a Store Alert, not in the Inbox. It's best to avoid using these types of notes unless you absolutely need to.
`$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );` `$note-&gt;set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );`
Admin Notes also support a few different layouts. You can specify `banner`, `plain`, or `thumbnail` as the layout. If you're interested in seeing the different layouts in action, take a look at [this simple plugin](https://gist.github.com/octaedro/864315edaf9c6a2a6de71d297be1ed88) that you can install to experiment with them. Admin Notes also support a few different layouts. You can specify `banner`, `plain`, or `thumbnail` as the layout. If you're interested in seeing the different layouts in action, take a look at [this simple plugin](https://gist.github.com/octaedro/864315edaf9c6a2a6de71d297be1ed88) that you can install to experiment with them.
We'll choose `plain` as our layout, but it's also the default, so we could leave this property alone and the effect would be the same. We'll choose `plain` as our layout, but it's also the default, so we could leave this property alone and the effect would be the same.
`$note->set_layout( 'plain' );` `$note-&gt;set_layout( 'plain' );`
If you have an image that you want to add to your Admin Note, you can specify it using the `set_image` function. This property ultimately renders as the `src` attribute on an `img` tag, so use a string here. If you have an image that you want to add to your Admin Note, you can specify it using the `set_image` function. This property ultimately renders as the `src` attribute on an `img` tag, so use a string here.
`$note->set_image( '' );` `$note-&gt;set_image( '' );`
Next, we'll set the values for our Admin Note's `name` and `source` properties. As a best practice, you should store your extension's name (i.e. its slug) in the `source` property of the note. You can use the `name` property to support multiple sub-types of notes. This gives you a handy way of namespacing your notes and managing them at both a high and low level. Next, we'll set the values for our Admin Note's `name` and `source` properties. As a best practice, you should store your extension's name (i.e. its slug) in the `source` property of the note. You can use the `name` property to support multiple sub-types of notes. This gives you a handy way of namespacing your notes and managing them at both a high and low level.
```php ```php
$note->set_source( 'inbox-note-example'); $note-&gt;set_source( 'inbox-note-example');
$note->set_name( self::NOTE_NAME ); $note-&gt;set_name( self::NOTE_NAME );
``` ```
Admin Notes can support 0, 1, or 2 actions (buttons). You can use these actions to capture events that trigger asynchronous processes or help the merchant navigate to a particular view to complete a step, or even simply to provide an external link for further information. The `add_action()` function takes up to three arguments. The first is the action name, which can be used for event handling, the second renders as a label for the action's button, and the third is an optional URL for actions that require navigation. Admin Notes can support 0, 1, or 2 actions (buttons). You can use these actions to capture events that trigger asynchronous processes or help the merchant navigate to a particular view to complete a step, or even simply to provide an external link for further information. The `add_action()` function takes up to three arguments. The first is the action name, which can be used for event handling, the second renders as a label for the action's button, and the third is an optional URL for actions that require navigation.
```php ```php
$note->add_action( $note-&gt;add_action(
'settings', 'Open Settings', '?page=wc-settings&tab=general' 'settings', 'Open Settings', '?page=wc-settings&tab=general'
); );
$note->add_action( $note-&gt;add_action(
'learn_more', 'Learn More', 'https://example.com' 'learn_more', 'Learn More', 'https://example.com'
); );
``` ```

View File

@ -3,7 +3,7 @@ post_title: Integrating with coming soon mode
tags: how-to, coming-soon tags: how-to, coming-soon
--- ---
This guide provides examples for third-party developers and hosting providers on how to integrate their systems with WooCommerce's coming soon mode. For more details, please read the [developer blog post](https://developer.woocommerce.com/2024/06/18/introducing-coming-soon-mode/). This guide provides examples for third-party developers and hosting providers on how to integrate their systems with WooCommerce's coming soon mode. For more details, please read the [developer blog post](https://developer.woocommerce.com/2024/06/18/introducing-coming-soon-mode/). For site visibility settings, please refer to the [admin documentation](https://woocommerce.com/document/configuring-woocommerce-settings/coming-soon-mode/).
## Introduction ## Introduction
@ -49,8 +49,8 @@ function sync_coming_soon_to_other_plugins( $old_value, $new_value, $option ) {
$is_enabled = $new_value === 'yes'; $is_enabled = $new_value === 'yes';
// Implement your logic to sync coming soon status. // Implement your logic to sync coming soon status.
if ( function_exists( 'set_your_plugin_status' ) ) { if ( function_exists( 'your_plugin_set_coming_soon' ) ) {
set_your_plugin_status( $is_enabled ); your_plugin_set_coming_soon( $is_enabled );
} }
} }
``` ```
@ -84,8 +84,8 @@ function sync_coming_soon_to_other_plugins( $old_value, $new_value, $option ) {
$is_enabled = $new_value === 'yes'; $is_enabled = $new_value === 'yes';
// Implement your logic to sync coming soon status. // Implement your logic to sync coming soon status.
if ( function_exists( 'set_your_plugin_status' ) ) { if ( function_exists( 'your_plugin_set_coming_soon' ) ) {
set_your_plugin_status( $is_enabled ); your_plugin_set_coming_soon( $is_enabled );
} }
} }
@ -108,6 +108,38 @@ function sync_coming_soon_from_other_plugins( $is_enabled ) {
} }
``` ```
#### One-way binding with option override
We could also programmatically bind the coming soon option from another plugin by overriding the `woocommerce_coming_soon` option. This is advantageous since it simplifies state management and prevents possible out-of-sync issues.
In the following example, we're binding the coming soon option from another plugin by overriding the `woocommerce_coming_soon` option.
```php
add_filter( 'pre_option_woocommerce_coming_soon', 'override_option_woocommerce_coming_soon' );
function override_option_woocommerce_coming_soon( $current_value ) {
// Implement your logic to sync coming soon status.
if ( function_exists( 'your_plugin_is_coming_soon' ) ) {
return your_plugin_is_coming_soon() ? 'yes' : 'no';
}
return $current_value;
}
add_filter( 'pre_update_option_woocommerce_coming_soon', 'override_update_woocommerce_coming_soon', 10, 2 );
function override_update_woocommerce_coming_soon( $new_value, $old_value ) {
// Check user capability.
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( 'You do not have sufficient permissions to access this page.' );
}
// Implement your logic to sync coming soon status.
if ( function_exists( 'your_plugin_set_coming_soon' ) ) {
your_plugin_set_coming_soon( $new_value === 'yes' );
}
}
```
### Custom exclusions filter ### Custom exclusions filter
It is possible for developers to add custom exclusions that bypass the coming soon protection. This is useful for exclusions like always bypassing the screen on a specific IP address, or making a specific landing page available. It is possible for developers to add custom exclusions that bypass the coming soon protection. This is useful for exclusions like always bypassing the screen on a specific IP address, or making a specific landing page available.
@ -134,3 +166,35 @@ add_filter( 'woocommerce_coming_soon_exclude', function( $is_excluded ) {
return $is_excluded; return $is_excluded;
}, 10 ); }, 10 );
``` ```
#### Custom share links
The following example shows how to integrate with a custom share code. We recommend using cookies or other storage to persist the access when users navigate across the site:
```php
add_filter( 'woocommerce_coming_soon_exclude', function( $exclude ) {
// Implement your logic to get and validate share code.
if ( function_exists( 'your_plugin_get_share_code' ) && function_exists( 'your_plugin_is_valid_share_code' ) ) {
$share_code = your_plugin_get_share_code();
if ( your_plugin_is_valid_share_code( $share_code ) ) {
return true;
}
}
return $exclude;
} );
```
### Extend "Apply to store pages only" setting
When using the `Apply to store pages only` setting, you may want to add a custom page to the list of store pages which will be restricted by coming soon mode. You can use the following example to add a custom page:
```php
add_filter( 'woocommerce_store_pages', function( $pages ) {
$page = get_page_by_path( 'your-page-slug' );
if ( $page ) {
$pages[] = $page->ID;
}
return $pages;
} );
```

View File

@ -87,7 +87,7 @@ Also, we didn't see a noticeable negative performance impact when keeping synchr
### Switch to HPOS as authoritative ### Switch to HPOS as authoritative
It's time to switch to HPOS. Go to **WooCommerce > Settings > Advanced > Features** and set HPOS to be authoritative (select **Use the WooCommerce orders tables**"). It's time to switch to HPOS. Go to **WooCommerce > Settings > Advanced > Features** and set HPOS to be authoritative (select "**Use the WooCommerce orders tables**").
As mentioned above, don't turn off synchronization yet. If there are any issues, the system can be instantaneously reverted to the posts table, resulting in no downtime. As mentioned above, don't turn off synchronization yet. If there are any issues, the system can be instantaneously reverted to the posts table, resulting in no downtime.
@ -107,7 +107,7 @@ We disable sync on read first because it demands more resources. If your site is
### Switch off sync on write ### Switch off sync on write
If everything is working as expected, you can disable sync on write as well. Given sync on read was already disabled, you can disable sync altogether from the settings. As usual, go to **WooCommerce > Settings > Advanced > Features**, and uncheck **Enable compatibility mode"**. If everything is working as expected, you can disable sync on write as well. Given sync on read was already disabled, you can disable sync altogether from the settings. As usual, go to **WooCommerce > Settings > Advanced > Features**, and uncheck **"Enable compatibility mode"**.
On our high-volume site, we fully disabled sync after 1 week. We still run some manual synchronization (via `wp wc cot sync`) periodically so that we have the opportunity to fall back to posts immediately should anything happen. On our high-volume site, we fully disabled sync after 1 week. We still run some manual synchronization (via `wp wc cot sync`) periodically so that we have the opportunity to fall back to posts immediately should anything happen.
@ -118,11 +118,11 @@ Now with synchronization fully disabled, test out various critical flows, check
### Review: Phase 3 Checklist ### Review: Phase 3 Checklist
1. [ ] Plan to be online and monitoring your live site for a period of time. 1. [ ] Plan to be online and monitoring your live site for a period of time.
2. [ ] Enable synchronization with posts set as authoritative: in **WooCommerce > Settings > Advanced > Features** > select **Use the WordPress posts tables**". 2. [ ] Enable synchronization with posts set as authoritative: in **WooCommerce > Settings > Advanced > Features** > select "**Use the WordPress posts tables**".
3. [ ] Start migration via CLI using the `wp wc cot sync` command. 3. [ ] Start migration via CLI using the `wp wc cot sync` command.
4. [ ] Monitor for errors during migration; halt or resume as necessary. 4. [ ] Monitor for errors during migration; halt or resume as necessary.
5. [ ] Verify migrated data integrity using the verify command `wp wc cot verify_cot_data`. 5. [ ] Verify migrated data integrity using the verify command `wp wc cot verify_cot_data`.
6. [ ] Enable synchronization with HPOS set as authoritative: in **WooCommerce > Settings > Advanced > Features** > select Use the **WooCommerce orders tables**". 6. [ ] Enable synchronization with HPOS set as authoritative: in **WooCommerce > Settings > Advanced > Features** > select "Use the **WooCommerce orders tables**".
7. [ ] Test all critical flows, perform checkouts with multiple payment methods, and verify order data accuracy. 7. [ ] Test all critical flows, perform checkouts with multiple payment methods, and verify order data accuracy.
8. [ ] Monitor support tickets for any issues. 8. [ ] Monitor support tickets for any issues.
9. [ ] Disable synchronization on read using the provided snippet: `add_filter( 'woocommerce_hpos_enable_sync_on_read', '__return_false' );` 9. [ ] Disable synchronization on read using the provided snippet: `add_filter( 'woocommerce_hpos_enable_sync_on_read', '__return_false' );`

View File

@ -20,7 +20,7 @@ For WooCommerce extensions, performance optimization means ensuring that your co
## Benchmarking Performance ## Benchmarking Performance
Setting clear performance benchmarks is essential for development and continuous improvement of WooCommerce extensions. A recommended performance standard is achieving a Chrome Core Web Vitals "Performance" score of 90 or above on Woo Express, using tools like the [Chrome Lighthouse](https://developer.chrome.com/docs/lighthouse/overview/). Setting clear performance benchmarks is essential for development and continuous improvement of WooCommerce extensions. A recommended performance standard is achieving a Chrome Core Web Vitals "Performance" score of 90 or above on a simple Woo site, using tools like the [Chrome Lighthouse](https://developer.chrome.com/docs/lighthouse/overview/).
### Using Accessible Tools for Benchmarking ### Using Accessible Tools for Benchmarking

View File

@ -10,13 +10,13 @@ tags: how-to
This event is triggered when Product Collection block was rendered or re-rendered (e.g. due to page change). This event is triggered when Product Collection block was rendered or re-rendered (e.g. due to page change).
### `detail` parameters ### `wc-blocks_product_list_rendered` - `detail` parameters
| Parameter | Type | Default value | Description | | Parameter | Type | Default value | Description |
| ------------------ | ------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------------ | ------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `collection` | string | `undefined` | Collection type. It's `undefined` for "create your own" collections as the type is not specified. For other Core collections it can be one of type: `woocommerce/product-collection/best-sellers`, `woocommerce/product-collection/featured`, `woocommerce/product-collection/new-arrivals`, `woocommerce/product-collection/on-sale`, `woocommerce/product-collection/top-rated`. For custom collections it will hold their name. | | `collection` | string | `undefined` | Collection type. It's `undefined` for "create your own" collections as the type is not specified. For other Core collections it can be one of type: `woocommerce/product-collection/best-sellers`, `woocommerce/product-collection/featured`, `woocommerce/product-collection/new-arrivals`, `woocommerce/product-collection/on-sale`, `woocommerce/product-collection/top-rated`. For custom collections it will hold their name. |
### Example usage ### `wc-blocks_product_list_rendered` - Example usage
```javascript ```javascript
window.document.addEventListener( window.document.addEventListener(
@ -27,3 +27,27 @@ window.document.addEventListener(
} }
); );
``` ```
## Event: `wc-blocks_viewed_product`
This event is triggered when some blocks are clicked in order to view product (redirect to product page).
### `wc-blocks_viewed_product` - `detail` parameters
| Parameter | Type | Default value | Description |
| ------------------ | ------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `collection` | string | `undefined` | Collection type. It's `undefined` for "create your own" collections as the type is not specified. For other Core collections it can be one of type: `woocommerce/product-collection/best-sellers`, `woocommerce/product-collection/featured`, `woocommerce/product-collection/new-arrivals`, `woocommerce/product-collection/on-sale`, `woocommerce/product-collection/top-rated`. For custom collections it will hold their name. |
| `productId` | number | | Product ID |
### `wc-blocks_viewed_product` Example usage
```javascript
window.document.addEventListener(
'wc-blocks_viewed_product',
( e ) => {
const { collection, productId } = e.detail;
console.log( collection ) // -> collection name, e.g. "woocommerce/product-collection/featured" or undefined for default one
console.log( productId ) // -> product ID, e.g. 34
}
);
```

View File

@ -6,20 +6,20 @@ tags: how-to
# Register Product Collection # Register Product Collection
The `__experimentalRegisterProductCollection` function is part of the `@woocommerce/blocks-registry` package. This function allows third party developers to register a new collection. This function accepts most of the arguments that are accepted by [Block Variation](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-variations/#defining-a-block-variation). The `__experimentalRegisterProductCollection` function is part of the `@woocommerce/blocks-registry` package. This function allows third party developers to register a new collection. This function accepts most of the arguments that are accepted by [Block Variation](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-variations/#defining-a-block-variation).
> [!WARNING] > [!WARNING]
> It's experimental and may change in the future. Please use it with caution. > It's experimental and may change in the future. Please use it with caution.
**There are two ways to use this function:** **There are two ways to use this function:**
1. Using `@woocommerce/dependency-extraction-webpack-plugin` in a Webpack configuration: This will allow you to import the function from the package & use it in your code. For example: 1. Using `@woocommerce/dependency-extraction-webpack-plugin` in a Webpack configuration: This will allow you to import the function from the package & use it in your code. For example:
```tsx ```tsx
import { __experimentalRegisterProductCollection } from "@woocommerce/blocks-registry"; import { __experimentalRegisterProductCollection } from "@woocommerce/blocks-registry";
``` ```
2. Using the global `wc` object: This will allow you to use the function using the global JS object without importing it. For example: 2. Using the global `wc` object: This will allow you to use the function using the global JS object without importing it. For example:
```tsx ```tsx
wc.wcBlocksRegistry.__experimentalRegisterProductCollection({ wc.wcBlocksRegistry.__experimentalRegisterProductCollection({
@ -113,7 +113,7 @@ __experimentalRegisterProductCollection({
}); });
``` ```
As you can see in the example above, we are registering a new collection with the name `woocommerce/product-collection/my-custom-collection` & title `My Custom Collection`. Here is screenshot of how it will look like: As you can see in the example above, we are registering a new collection with the name `woocommerce/product-collection/my-custom-collection` & title `My Custom Collection`. Here is screenshot of how it will look like:
![image](https://github.com/woocommerce/woocommerce/assets/16707866/7fddbc02-a6cd-494e-b2f4-13dd5ef9cf96) ![image](https://github.com/woocommerce/woocommerce/assets/16707866/7fddbc02-a6cd-494e-b2f4-13dd5ef9cf96)
### Example 2: Register a new collection with a preview ### Example 2: Register a new collection with a preview

View File

@ -4,7 +4,7 @@ category_slug: testing
post_title: Testing post_title: Testing
--- ---
Properly setting up your test environment and writing tests when contributing to WooCommrece core are essential parts of our development pipeline. The links below are also included in our [Contributing Guidelines](https://github.com/woocommerce/woocommerce/blob/trunk/.github/CONTRIBUTING.md) on GitHub. Properly setting up your test environment and writing tests when contributing to WooCommerce core are essential parts of our development pipeline. The links below are also included in our [Contributing Guidelines](https://github.com/woocommerce/woocommerce/blob/trunk/.github/CONTRIBUTING.md) on GitHub.
If you have any questions about testing please reach out to the developer community in our public channels([Developer Blog](https://developer.woocommerce.com/blog/), [GitHub Discussions](https://github.com/woocommerce/woocommerce/discussions), or [Community Slack](https://woocommerce.com/community-slack/)). If you have any questions about testing please reach out to the developer community in our public channels([Developer Blog](https://developer.woocommerce.com/blog/), [GitHub Discussions](https://github.com/woocommerce/woocommerce/discussions), or [Community Slack](https://woocommerce.com/community-slack/)).

View File

@ -22,7 +22,7 @@ Below is video walkthrough showing how one may go about updating the template fi
## Template list ## Template list
The various template files on your WooCommerce site can be found via an FTP client or your hosts file manager, in `/wp-content/plugins/woocommerce/templates/`. Below are links to the current and earlier versions of the WooCommerce template files on Github, where you can view the code exactly as it appears in those files: The various template files on your WooCommerce site can be found via an FTP client or your hosts file manager, in `/wp-content/plugins/woocommerce/templates/`. Below are links to the current and earlier versions of the WooCommerce template files on Github, where you can view the code exactly as it appears in those files:
| Latest Version | Files | | Latest Version | Files |
| -------------- | ----- | | -------------- | ----- |
@ -96,7 +96,7 @@ Below are the links to the files of all major previous WooCommerce versions:
## Changing Templates via Hooks ## Changing Templates via Hooks
When you open a template file, you will notice they all contain _hooks_ that allow you to add/move content without needing to edit template files themselves. Hooks are a way for one piece of code to interact/modify another piece of code at specific, pre-defined spots. This method allows implementing a code snippet that “hooks” into a particular a theme location. It avoids upgrade issues, as the template files can be left completely untouched and doesn't require a child theme to be configured. When you open a template file, you will notice they all contain _hooks_ that allow you to add/move content without needing to edit template files themselves. Hooks are a way for one piece of code to interact/modify another piece of code at specific, pre-defined spots. This method allows implementing a code snippet that "hooks" into a particular a theme location. It avoids upgrade issues, as the template files can be left completely untouched and doesn't require a child theme to be configured.
Let's take a look at [/wp-content/plugins/woocommerce/templates/emails/admin-new-order.php](https://github.com/woocommerce/woocommerce/blob/8.9.0/plugins/woocommerce/templates/emails/admin-new-order.php) and see what a hook looks like. Starting on line 30, we see the following code, which is responsible for producing the order details section of the New Order email. Let's take a look at [/wp-content/plugins/woocommerce/templates/emails/admin-new-order.php](https://github.com/woocommerce/woocommerce/blob/8.9.0/plugins/woocommerce/templates/emails/admin-new-order.php) and see what a hook looks like. Starting on line 30, we see the following code, which is responsible for producing the order details section of the New Order email.
@ -137,7 +137,7 @@ The copied file will now override the WooCommerce default template file, so you
--- ---
**Note** A (desirable) side-effect of your templates being upgrade-safe is that WooCommerce core templates will update, but your custom overrides will not. You may occassionally see notices in your System Status report that says, e.g. “version 3.5.0 is out of date. The core version is 3.7.0″. Should that happen, follow the Fixing Outdated WooCommerce Templates guide to bring them in line. **Note** A (desirable) side-effect of your templates being upgrade-safe is that WooCommerce core templates will update, but your custom overrides will not. You may occassionally see notices in your System Status report that says, e.g. "version 3.5.0 is out of date. The core version is 3.7.0". Should that happen, follow the Fixing Outdated WooCommerce Templates guide to bring them in line.
--- ---

View File

@ -7,10 +7,7 @@ menu_title: Navigation
Examples: Examples:
- If your extension is extending a component within WooCommerce, it should live within either the Extensions navigation drawer (in Woo Express stores), or directly within that category's section. - If your extension is extending a component within WooCommerce, it should live directly within that category's section.
Extensions drawer (Woo Express)
![Navigation extensions drawer](https://developer.woocommerce.com/docs/wp-content/uploads/sites/3/2024/01/Image-1224x572-1.png)
![Navigation category](https://developer.woocommerce.com/docs/wp-content/uploads/sites/3/2024/01/Image-1242x764-1.png) ![Navigation category](https://developer.woocommerce.com/docs/wp-content/uploads/sites/3/2024/01/Image-1242x764-1.png)

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Update storybook file format in support with Storybook 7 story indexer.

View File

@ -132,18 +132,10 @@
"devDependencies": { "devDependencies": {
"@babel/core": "^7.23.5", "@babel/core": "^7.23.5",
"@babel/runtime": "^7.23.5", "@babel/runtime": "^7.23.5",
"@storybook/addon-actions": "6.5.17-alpha.0",
"@storybook/addon-console": "^1.2.3", "@storybook/addon-console": "^1.2.3",
"@storybook/addon-controls": "6.5.17-alpha.0", "@storybook/addon-docs": "7.6.19",
"@storybook/addon-docs": "6.5.17-alpha.0", "@storybook/addon-links": "7.6.19",
"@storybook/addon-knobs": "^6.4.0",
"@storybook/addon-links": "6.5.17-alpha.0",
"@storybook/addons": "6.5.17-alpha.0",
"@storybook/api": "6.5.17-alpha.0",
"@storybook/components": "6.5.17-alpha.0",
"@storybook/core-events": "6.5.17-alpha.0",
"@storybook/react": "6.5.17-alpha.0", "@storybook/react": "6.5.17-alpha.0",
"@storybook/theming": "6.5.17-alpha.0",
"@testing-library/dom": "8.11.3", "@testing-library/dom": "8.11.3",
"@testing-library/jest-dom": "5.16.2", "@testing-library/jest-dom": "5.16.2",
"@testing-library/react": "12.1.3", "@testing-library/react": "12.1.3",

View File

@ -7,7 +7,7 @@ import { createElement } from '@wordpress/element';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { Basic } from '../stories/index'; import { Basic } from '../stories/compare-filter.story';
import { CompareFilter } from '../index'; import { CompareFilter } from '../index';
import Search from '../../search'; import Search from '../../search';
import productAutocompleter from '../../search/autocompleters/product'; import productAutocompleter from '../../search/autocompleters/product';

View File

@ -8,7 +8,7 @@ import { createElement } from '@wordpress/element';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { Basic } from '../stories/index'; import { Basic } from '../stories/filter-picker.story';
import FilterPicker from '../index'; import FilterPicker from '../index';
import Search from '../../search'; import Search from '../../search';
import productAutocompleter from '../../search/autocompleters/product'; import productAutocompleter from '../../search/autocompleters/product';

View File

@ -8,7 +8,7 @@ import React from 'react';
* Internal dependencies * Internal dependencies
*/ */
import { ImageGallery, ImageGalleryItem } from '../'; import { ImageGallery, ImageGalleryItem } from '../';
import { MockMediaUpload } from '../../media-uploader/stories/index.tsx'; import { MockMediaUpload } from '../../media-uploader/stories/mock-media-uploader';
export const Basic: React.FC = () => { export const Basic: React.FC = () => {
return ( return (

View File

@ -2,7 +2,7 @@
* External dependencies * External dependencies
*/ */
import React, { createElement } from 'react'; import React, { createElement } from 'react';
import { Modal, Notice } from '@wordpress/components'; import { Notice } from '@wordpress/components';
import { MediaItem } from '@wordpress/media-utils'; import { MediaItem } from '@wordpress/media-utils';
import { useState } from '@wordpress/element'; import { useState } from '@wordpress/element';
import { cloudUpload } from '@wordpress/icons'; import { cloudUpload } from '@wordpress/icons';
@ -12,59 +12,7 @@ import { cloudUpload } from '@wordpress/icons';
*/ */
import { MediaUploader } from '../'; import { MediaUploader } from '../';
import { File } from '../types'; import { File } from '../types';
import { MockMediaUpload } from './mock-media-uploader';
const MockMediaUpload = ( { onSelect, render } ) => {
const [ isOpen, setOpen ] = useState( false );
return (
<>
{ render( {
open: () => setOpen( true ),
} ) }
{ isOpen && (
<Modal
title="Media Modal"
onRequestClose={ ( event ) => {
setOpen( false );
event.stopPropagation();
} }
>
<p>
Use the default built-in{ ' ' }
<code>MediaUploadComponent</code> prop to render the WP
Media Modal.
</p>
{ Array( ...Array( 3 ) ).map( ( n, i ) => {
return (
<button
key={ i }
onClick={ ( event ) => {
onSelect( {
alt: 'Random',
url: `https://picsum.photos/200?i=${ i }`,
} );
setOpen( false );
event.stopPropagation();
} }
style={ {
marginRight: '16px',
} }
>
<img
src={ `https://picsum.photos/200?i=${ i }` }
alt="Random"
style={ {
maxWidth: '100px',
} }
/>
</button>
);
} ) }
</Modal>
) }
</>
);
};
const ImageGallery = ( { images }: { images: File[] } ) => { const ImageGallery = ( { images }: { images: File[] } ) => {
return ( return (

View File

@ -0,0 +1,59 @@
/**
* External dependencies
*/
import React, { createElement } from 'react';
import { Modal } from '@wordpress/components';
import { useState } from '@wordpress/element';
export const MockMediaUpload = ( { onSelect, render } ) => {
const [ isOpen, setOpen ] = useState( false );
return (
<>
{ render( {
open: () => setOpen( true ),
} ) }
{ isOpen && (
<Modal
title="Media Modal"
onRequestClose={ ( event ) => {
setOpen( false );
event.stopPropagation();
} }
>
<p>
Use the default built-in{ ' ' }
<code>MediaUploadComponent</code> prop to render the WP
Media Modal.
</p>
{ Array( ...Array( 3 ) ).map( ( n, i ) => {
return (
<button
key={ i }
onClick={ ( event ) => {
onSelect( {
alt: 'Random',
url: `https://picsum.photos/200?i=${ i }`,
} );
setOpen( false );
event.stopPropagation();
} }
style={ {
marginRight: '16px',
} }
>
<img
src={ `https://picsum.photos/200?i=${ i }` }
alt="Random"
style={ {
maxWidth: '100px',
} }
/>
</button>
);
} ) }
</Modal>
) }
</>
);
};

View File

@ -11,7 +11,12 @@ import {
} from '@wordpress/element'; } from '@wordpress/element';
import { SyntheticEvent, useCallback } from 'react'; import { SyntheticEvent, useCallback } from 'react';
import { useDispatch, useSelect } from '@wordpress/data'; import { useDispatch, useSelect } from '@wordpress/data';
import { PLUGINS_STORE_NAME, InstallPluginsResponse } from '@woocommerce/data'; import { PLUGINS_STORE_NAME } from '@woocommerce/data';
import type {
InstallPluginsResponse,
PluginSelectors,
PluginActions,
} from '@woocommerce/data';
type PluginsProps = { type PluginsProps = {
onComplete: ( onComplete: (
@ -52,10 +57,14 @@ export const Plugins = ( {
const [ hasErrors, setHasErrors ] = useState( false ); const [ hasErrors, setHasErrors ] = useState( false );
// Tracks action so that multiple instances of this button don't all light up when one is clicked // Tracks action so that multiple instances of this button don't all light up when one is clicked
const [ hasBeenClicked, setHasBeenClicked ] = useState( false ); const [ hasBeenClicked, setHasBeenClicked ] = useState( false );
const { installAndActivatePlugins } = useDispatch( PLUGINS_STORE_NAME ); const { installAndActivatePlugins }: PluginActions =
useDispatch( PLUGINS_STORE_NAME );
const { isRequesting } = useSelect( ( select ) => { const { isRequesting } = useSelect( ( select ) => {
const { getActivePlugins, getInstalledPlugins, isPluginsRequesting } = const {
select( PLUGINS_STORE_NAME ); getActivePlugins,
getInstalledPlugins,
isPluginsRequesting,
}: PluginSelectors = select( PLUGINS_STORE_NAME );
return { return {
isRequesting: isRequesting:
@ -64,7 +73,7 @@ export const Plugins = ( {
activePlugins: getActivePlugins(), activePlugins: getActivePlugins(),
installedPlugins: getInstalledPlugins(), installedPlugins: getInstalledPlugins(),
}; };
} ); }, [] );
const handleErrors = useCallback( const handleErrors = useCallback(
( errors: unknown, response: InstallPluginsResponse ) => { ( errors: unknown, response: InstallPluginsResponse ) => {

View File

@ -13,7 +13,7 @@ import { useDispatch } from '@wordpress/data';
import { Plugins } from '../index'; import { Plugins } from '../index';
jest.mock( '@wordpress/data', () => ( { jest.mock( '@wordpress/data', () => ( {
...jest.requireActual( '@wordpress/data' ), __esModule: true,
useDispatch: jest useDispatch: jest
.fn() .fn()
.mockReturnValue( { installAndActivatePlugins: jest.fn() } ), .mockReturnValue( { installAndActivatePlugins: jest.fn() } ),

View File

@ -12,7 +12,6 @@ import * as actions from './actions';
import { STORE_NAME } from './constants'; import { STORE_NAME } from './constants';
export const store = createReduxStore( STORE_NAME, { export const store = createReduxStore( STORE_NAME, {
// @ts-expect-error reducer has correct format.
reducer, reducer,
selectors, selectors,
actions, actions,

View File

@ -8,6 +8,7 @@ export type ProductFieldDefinition = {
type?: HTMLInputTypeAttribute; type?: HTMLInputTypeAttribute;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
render?: ComponentType; render?: ComponentType;
attributes?: Record< string, string >;
}; };
export type ProductFieldState = { export type ProductFieldState = {

View File

@ -43,7 +43,7 @@ export const EditorWritingFlow = ( {
editorMode: __unstableGetEditorMode(), editorMode: __unstableGetEditorMode(),
selectedBlockClientIds: getSelectedBlockClientIds(), selectedBlockClientIds: getSelectedBlockClientIds(),
}; };
} ); }, [] );
// This is a workaround to prevent focusing the block on initialization. // This is a workaround to prevent focusing the block on initialization.
// Changing to a mode other than "edit" ensures that no initial position // Changing to a mode other than "edit" ensures that no initial position

View File

@ -1,107 +0,0 @@
/**
* External dependencies
*/
import { date, text } from '@storybook/addon-knobs';
import GridIcon from 'gridicons';
/**
* Internal dependencies
*/
import Timeline, { orderByOptions } from '../';
export default {
title: 'WooCommerce Admin/components/Timeline',
component: Timeline,
};
export const Empty = () => <Timeline />;
const itemDate = ( label, value ) => {
const d = date( label, value );
return new Date( d );
};
export const Filled = () => (
<Timeline
orderBy={ orderByOptions.DESC }
items={ [
{
date: itemDate(
'event 1 date',
new Date( 2020, 0, 20, 1, 30 )
),
body: [
<p key={ '1' }>
{ text( 'event 1, first event', 'p element in body' ) }
</p>,
text( 'event 1, second event', 'string in body' ),
],
headline: (
<p>{ text( 'event 1, headline', 'p tag in headline' ) }</p>
),
icon: (
<GridIcon
className={ 'is-success' }
icon={ text( 'event 1 gridicon', 'checkmark' ) }
size={ 16 }
/>
),
hideTimestamp: true,
},
{
date: itemDate(
'event 2 date',
new Date( 2020, 0, 20, 23, 45 )
),
body: [],
headline: (
<span>
{ text( 'event 2, headline', 'span in headline' ) }
</span>
),
icon: (
<GridIcon
className={ 'is-warning' }
icon={ text( 'event 2 gridicon', 'refresh' ) }
size={ 16 }
/>
),
},
{
date: itemDate(
'event 3 date',
new Date( 2020, 0, 22, 15, 13 )
),
body: [
<span key={ '1' }>
{ text( 'event 3, second event', 'span in body' ) }
</span>,
],
headline: text( 'event 3, headline', 'string in headline' ),
icon: (
<GridIcon
className={ 'is-error' }
icon={ text( 'event 3 gridicon', 'cross' ) }
size={ 16 }
/>
),
},
{
date: itemDate(
'event 4 date',
new Date( 2020, 0, 17, 1, 45 )
),
headline: text(
'event 4, headline',
'undefined body and string headline'
),
icon: (
<GridIcon
icon={ text( 'event 4 gridicon', 'cross' ) }
size={ 16 }
/>
),
},
] }
/>
);

View File

@ -0,0 +1,71 @@
/**
* External dependencies
*/
import GridIcon from 'gridicons';
/**
* Internal dependencies
*/
import Timeline, { orderByOptions } from '../';
export const Filled = () => {
return (
<Timeline
orderBy={ orderByOptions.DESC }
items={ [
{
date: new Date( 2020, 0, 20, 1, 30 ),
body: [
<p key="1">p element in body</p>,
'string in body',
],
headline: <p>p tag in headline</p>,
icon: (
<GridIcon
className="is-success"
icon="checkmark"
size={ 16 }
/>
),
hideTimestamp: true,
},
{
date: new Date( 2020, 0, 20, 23, 45 ),
body: [],
headline: <span>span in headline</span>,
icon: (
<GridIcon
className={ 'is-warning' }
icon="refresh"
size={ 16 }
/>
),
},
{
date: new Date( 2020, 0, 22, 15, 13 ),
body: [ <span key="1">span in body</span> ],
headline: 'string in headline',
icon: (
<GridIcon
className={ 'is-error' }
icon="cross"
size={ 16 }
/>
),
},
{
date: new Date( 2020, 0, 17, 1, 45 ),
headline: 'undefined body and string headline',
icon: <GridIcon icon="cross" size={ 16 } />,
},
] }
/>
);
};
export default {
title: 'WooCommerce Admin/components/Timeline',
component: Filled,
};
export const Empty = () => <Timeline />;

View File

@ -0,0 +1,4 @@
Significance: patch
Type: update
Add wooPaymentsIncentiveId to the TaskType additionalData.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Export plugin related data store types for selectors and actions.

View File

@ -85,6 +85,8 @@ export {
} from './product-form/types'; } from './product-form/types';
export * from './onboarding/types'; export * from './onboarding/types';
export * from './plugins/types'; export * from './plugins/types';
export { PluginSelectors } from './plugins/selectors';
export { ActionDispatchers as PluginActions } from './plugins/actions';
export * from './products/types'; export * from './products/types';
export type { export type {
PartialProductVariation, PartialProductVariation,

View File

@ -35,6 +35,7 @@ export type TaskType = {
stripeTaxActivated?: boolean; stripeTaxActivated?: boolean;
woocommerceTaxActivated?: boolean; woocommerceTaxActivated?: boolean;
woocommerceShippingActivated?: boolean; woocommerceShippingActivated?: boolean;
wooPaymentsIncentiveId?: string;
}; };
// Possibly added in DeprecatedTasks.mergeDeprecatedCallbackFunctions // Possibly added in DeprecatedTasks.mergeDeprecatedCallbackFunctions
isDeprecated?: boolean; isDeprecated?: boolean;

View File

@ -12,7 +12,8 @@ npm install @woocommerce/e2e-utils --save
## Usage ## Usage
Example: Example:
~~~js
```js
import { import {
shopper, shopper,
merchant, merchant,
@ -29,7 +30,7 @@ describe( 'Cart page', () => {
await expect( page ).toMatchElement( '.cart-empty', { text: 'Your cart is currently empty.' } ); await expect( page ).toMatchElement( '.cart-empty', { text: 'Your cart is currently empty.' } );
} ); } );
} ); } );
~~~ ```
### Retries ### Retries
@ -86,7 +87,6 @@ This package provides support for enabling retries in tests:
|----------|-------------|------------| |----------|-------------|------------|
| `addDownloadableProductPermission` | `productName` | Add a downloadable permission for product in order | | `addDownloadableProductPermission` | `productName` | Add a downloadable permission for product in order |
| `collapseAdminMenu` | `collapse` | Collapse or expand the WP admin menu | | `collapseAdminMenu` | `collapse` | Collapse or expand the WP admin menu |
| `dismissOnboardingWizard` | | Dismiss the onboarding wizard if present |
| `goToOrder` | `orderId` | Go to view a single order | | `goToOrder` | `orderId` | Go to view a single order |
| `goToProduct` | `productId` | Go to view a single product | | `goToProduct` | `productId` | Go to view a single product |
| `login` | | Log in as merchant | | `login` | | Log in as merchant |
@ -100,7 +100,6 @@ This package provides support for enabling retries in tests:
| `openPermalinkSettings` | | Go to Settings -> Permalinks | | `openPermalinkSettings` | | Go to Settings -> Permalinks |
| `openPlugins` | | Go to the Plugins screen | | `openPlugins` | | Go to the Plugins screen |
| `openSettings` | | Go to WooCommerce -> Settings | | `openSettings` | | Go to WooCommerce -> Settings |
| `runSetupWizard` | | Open the onboarding profiler |
| `updateOrderStatus` | `orderId, status` | Update the status of an order | | `updateOrderStatus` | `orderId, status` | Update the status of an order |
| `openEmailLog` | | Open the WP Mail Log page | | `openEmailLog` | | Open the WP Mail Log page |
| `openAnalyticsPage` | | Open any Analytics page | | `openAnalyticsPage` | | Open any Analytics page |
@ -210,7 +209,6 @@ There is a general utilities object `utils` with the following functions:
| `clickFilter` | `selector` | helper method that clicks on a list page filter | | `clickFilter` | `selector` | helper method that clicks on a list page filter |
| `clickTab` | `tabName` | Click on a WooCommerce -> Settings tab | | `clickTab` | `tabName` | Click on a WooCommerce -> Settings tab |
| `clickUpdateOrder` | `noticeText`, `waitForSave` | Helper method to click the Update button on the order details page | | `clickUpdateOrder` | `noticeText`, `waitForSave` | Helper method to click the Update button on the order details page |
| `completeOnboardingWizard` | | completes the onboarding wizard with some default settings |
| `createCoupon` | `couponAmount`, `couponType` | creates a basic coupon. Default amount is 5. Default coupon type is fixed discount. Returns the generated coupon code. | | `createCoupon` | `couponAmount`, `couponType` | creates a basic coupon. Default amount is 5. Default coupon type is fixed discount. Returns the generated coupon code. |
| `createGroupedProduct` | | creates a grouped product for the grouped product tests. Returns the product id. | | `createGroupedProduct` | | creates a grouped product for the grouped product tests. Returns the product id. |
| `createSimpleDownloadableProduct` | `name, downloadLimit, downloadName, price` | Create a simple downloadable product | | `createSimpleDownloadableProduct` | `name, downloadLimit, downloadName, price` | Create a simple downloadable product |

Some files were not shown because too many files have changed in this diff Show More