From a54c09d4bd0516cda508080541ee11d6a0a36849 Mon Sep 17 00:00:00 2001 From: Brent MacKinnon Date: Mon, 20 Nov 2023 13:00:39 -0400 Subject: [PATCH 001/271] Create gdpr-compliance.md --- docs/extension-development/gdpr-compliance.md | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 docs/extension-development/gdpr-compliance.md diff --git a/docs/extension-development/gdpr-compliance.md b/docs/extension-development/gdpr-compliance.md new file mode 100644 index 00000000000..b1f04ff6cff --- /dev/null +++ b/docs/extension-development/gdpr-compliance.md @@ -0,0 +1,74 @@ +# GDPR Compliance Guidelines for WooCommerce Extensions + +## Introduction + +The General Data Protection Regulation (GDPR) is in effect, granting EU residents increased rights over their personal data. Developers must ensure that WooCommerce extensions are compliant with these regulations. + +## Data Sharing and Collection + +### Third-Party Data Sharing + +- Assess and document any third-party data sharing. +- Obtain and manage user consent for data sharing. +- Link to third-party privacy policies in your plugin settings. + +### Data Collection + +- List the personal data your plugin collects. +- Secure consent for data collection and manage user preferences. +- Safeguard data storage and restrict access to authorized personnel. + +## Data Access and Storage + +### Accessing Personal Data + +- Specify what personal data your plugin accesses from WooCommerce orders. +- Justify the necessity for accessing each type of data. +- Control access to personal data based on user roles and permissions. + +### Storing Personal Data + +- Explain your data storage mechanisms and locations. +- Apply encryption to protect stored personal data. +- Perform regular security audits. + +## Personal Data Handling + +### Data Exporter and Erasure Hooks + +- Integrate data exporter and erasure hooks to comply with user requests. +- Create a user-friendly interface for data management requests. + +### Refusal of Data Erasure + +- Define clear protocols for instances where data erasure is refused. +- Communicate these protocols transparently to users. + +## Frontend and Backend Data Exposure + +### Data on the Frontend + +- Minimize personal data displayed on the site's frontend. +- Provide configurable settings for data visibility based on user status. + +### Data in REST API Endpoints + +- Ensure REST API endpoints are secure and disclose personal data only as necessary. +- Establish clear permissions for accessing personal data via the API. + +## Privacy Documentation and Data Management + +### Privacy Policy Documentation + +- Maintain an up-to-date privacy policy detailing your plugin’s data handling. +- Include browser storage methods and third-party data sharing in your documentation. + +### Data Cleanup + +- Implement data cleanup protocols for plugin uninstallation and deletion of orders/users. +- Automate personal data removal processes where appropriate. + +## Conclusion + +- Keep a record of GDPR compliance measures and make them accessible to users. +- Update your privacy policy regularly to align with any changes in data processing activities. From d8ec0f68e0c0dc400a901525d6f978775992dcd6 Mon Sep 17 00:00:00 2001 From: Brent MacKinnon Date: Mon, 20 Nov 2023 13:13:47 -0400 Subject: [PATCH 002/271] Create configuring-caching-plugins.md --- docs/utilities/configuring-caching-plugins.md | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 docs/utilities/configuring-caching-plugins.md diff --git a/docs/utilities/configuring-caching-plugins.md b/docs/utilities/configuring-caching-plugins.md new file mode 100644 index 00000000000..96a4a986ce8 --- /dev/null +++ b/docs/utilities/configuring-caching-plugins.md @@ -0,0 +1,100 @@ +# Configuring Caching Plugins for WooCommerce + +## Excluding Pages from the Cache + +Oftentimes if using caching plugins they’ll already exclude these pages. Otherwise make sure you exclude the following pages from the cache through your caching systems respective settings. + +- Cart +- My Account +- Checkout + +These pages need to stay dynamic since they display information specific to the current customer and their cart. + +## Excluding WooCommerce Session from the Cache + +If the caching system you’re using offers database caching, it might be helpful to exclude `_wc_session_` from being cached. This will be dependent on the plugin or host caching so refer to the specific instructions or docs for that system. + +## Excluding WooCommerce Cookies from the Cache + +Cookies in WooCommerce help track the products in your customers cart, can keep their cart in the database if they leave the site, and powers the recently viewed widget. Below is a list of the cookies WooCommerce uses for this, which you can exclude from caching. + +| COOKIE NAME | DURATION | PURPOSE | +| --- | --- | --- | +| woocommerce_cart_hash | session | Helps WooCommerce determine when cart contents/data changes. | +| woocommerce_items_in_cart | session | Helps WooCommerce determine when cart contents/data changes. | +| wp_woocommerce_session_ | 2 days | Contains a unique code for each customer so that it knows where to find the cart data in the database for each customer. | +| woocommerce_recently_viewed | session | Powers the Recent Viewed Products widget. | +| store_notice[notice id] | session | Allows customers to dismiss the Store Notice. | + +We’re unable to cover all options, but we have added some tips for the popular caching plugins. For more specific support, please reach out to the support team responsible for your caching integration. + +### W3 Total Cache Minify Settings + +Ensure you add ‘mfunc’ to the ‘Ignored comment stems’ option in the Minify settings. + +### WP-Rocket + +WooCommerce is fully compatible with WP-Rocket. Please ensure that the following pages (Cart, Checkout, My Account) are not to be cached in the plugin’s settings. + +We recommend avoiding JavaScript file minification. + +### WP Super Cache + +WooCommerce is natively compatible with WP Super Cache. WooCommerce sends information to WP Super Cache so that it doesn’t cache the Cart, Checkout, or My Account pages by default. + +### Varnish + +```varnish +if (req.url ~ "^/(cart|my-account|checkout|addons)") { + return (pass); +} +if ( req.url ~ "\\?add-to-cart=" ) { + return (pass); +} +``` + +# Troubleshooting + +## Why is my Varnish configuration not working in WooCommerce? + +Check out the following WordPress.org Support forum post on[ how cookies may be affecting your varnish coding](https://wordpress.org/support/topic/varnish-configuration-not-working-in-woocommerce). + +``` +Add this to vcl_recv above "if (req.http.cookie) {": + +# Unset Cookies except for WordPress admin and WooCommerce pages +if (!(req.url ~ "(wp-login|wp-admin|cart|my-account/*|wc-api*|checkout|addons|logout|lost-password|product/*)")) { +unset req.http.cookie; +} +# Pass through the WooCommerce dynamic pages +if (req.url ~ "^/(cart|my-account/*|checkout|wc-api/*|addons|logout|lost-password|product/*)") { +return (pass); +} +# Pass through the WooCommerce add to cart +if (req.url ~ "\?add-to-cart=" ) { +return (pass); +} +# Pass through the WooCommerce API +if (req.url ~ "\?wc-api=" ) { +return (pass); +} +# Block access to php admin pages via website +if (req.url ~ "^/phpmyadmin/.*$" || req.url ~ "^/phppgadmin/.*$" || req.url ~ "^/server-status.*$") { +error 403 "For security reasons, this URL is only accesible using localhost (127.0.0.1) as the hostname"; +} +# + +Add this to vcl_fetch: + +# Unset Cookies except for WordPress admin and WooCommerce pages +if ( (!(req.url ~ "(wp-(login|admin)|login|cart|my-account/*|wc-api*|checkout|addons|logout|lost-password|product/*)")) || (req.request == "GET") ) { +unset beresp.http.set-cookie; +} +# +``` + +## Why is my Password Reset stuck in a loop? + +This is due to the My Account page being cached, Some hosts with server-side caching don’t prevent my-account.php from being cached. + +If you’re unable to reset your password and keep being returned to the login screen, please speak to your host to make sure this page is being excluded from their caching. From 728389cd39f91057e01fb1d32f891f8049c25e27 Mon Sep 17 00:00:00 2001 From: Brent MacKinnon Date: Mon, 20 Nov 2023 13:16:47 -0400 Subject: [PATCH 003/271] Update configuring-caching-plugins.md --- docs/utilities/configuring-caching-plugins.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/utilities/configuring-caching-plugins.md b/docs/utilities/configuring-caching-plugins.md index 96a4a986ce8..9fdf2571ab2 100644 --- a/docs/utilities/configuring-caching-plugins.md +++ b/docs/utilities/configuring-caching-plugins.md @@ -53,13 +53,13 @@ if ( req.url ~ "\\?add-to-cart=" ) { } ``` -# Troubleshooting +## Troubleshooting -## Why is my Varnish configuration not working in WooCommerce? +### Why is my Varnish configuration not working in WooCommerce? Check out the following WordPress.org Support forum post on[ how cookies may be affecting your varnish coding](https://wordpress.org/support/topic/varnish-configuration-not-working-in-woocommerce). -``` +```text Add this to vcl_recv above "if (req.http.cookie) {": # Unset Cookies except for WordPress admin and WooCommerce pages @@ -93,7 +93,7 @@ unset beresp.http.set-cookie; # ``` -## Why is my Password Reset stuck in a loop? +### Why is my Password Reset stuck in a loop? This is due to the My Account page being cached, Some hosts with server-side caching don’t prevent my-account.php from being cached. From 254469ddbe9ba3cb8f9e21067b190def9d7d4585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Wed, 22 Nov 2023 16:37:52 -0300 Subject: [PATCH 004/271] update/product-editor-add-missing-desc --- .../ProductTemplates/SimpleProductTemplate.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php b/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php index 7c5dc12bbd4..3ae7bde73a8 100644 --- a/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php +++ b/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php @@ -376,6 +376,7 @@ class SimpleProductTemplate extends AbstractProductFormTemplate implements Produ 'order' => 10, 'attributes' => array( 'title' => __( 'Product catalog', 'woocommerce' ), + 'description' => __( 'Help customers find this product by assigning it to categories, adding extra details, and managing its visibility in your store and other channels.', 'woocommerce' ), ), ) ); From de1b1c89cf5d8f733b5913247fddd9a8fb3e6e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Wed, 22 Nov 2023 15:04:32 -0300 Subject: [PATCH 005/271] add tooltip helo to Organization/Attributes title --- .../ProductTemplates/SimpleProductTemplate.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php b/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php index 3ae7bde73a8..caa7c46f110 100644 --- a/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php +++ b/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php @@ -454,6 +454,7 @@ class SimpleProductTemplate extends AbstractProductFormTemplate implements Produ 'order' => 20, 'attributes' => array( 'title' => __( 'Attributes', 'woocommerce' ), + 'description' => __( 'Add descriptive pieces of information that customers can use to filter and search for this product. Learn more.', 'woocommerce' ), ), ) ); From 4faa12de99752655825460583d716f50b0163c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Wed, 22 Nov 2023 15:06:06 -0300 Subject: [PATCH 006/271] changelog --- .../changelog/update-product-editor-add-missing-desc | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 packages/js/product-editor/changelog/update-product-editor-add-missing-desc diff --git a/packages/js/product-editor/changelog/update-product-editor-add-missing-desc b/packages/js/product-editor/changelog/update-product-editor-add-missing-desc new file mode 100644 index 00000000000..3e613942e70 --- /dev/null +++ b/packages/js/product-editor/changelog/update-product-editor-add-missing-desc @@ -0,0 +1,4 @@ +Significance: patch +Type: add + +[Product Blocks editor]: add tooltip help to Organization/Product catalog and Attributes From c9a211c6fb4c40cc73e37746e2aa2cbb8994f67f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Wed, 22 Nov 2023 16:13:07 -0300 Subject: [PATCH 007/271] move changelog file to the proper place --- .../woocommerce}/changelog/update-product-editor-add-missing-desc | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {packages/js/product-editor => plugins/woocommerce}/changelog/update-product-editor-add-missing-desc (100%) diff --git a/packages/js/product-editor/changelog/update-product-editor-add-missing-desc b/plugins/woocommerce/changelog/update-product-editor-add-missing-desc similarity index 100% rename from packages/js/product-editor/changelog/update-product-editor-add-missing-desc rename to plugins/woocommerce/changelog/update-product-editor-add-missing-desc From 7a98a3839935f0d9da88a4c493bf7f0c5e82c1dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Su=C3=A1rez?= Date: Wed, 22 Nov 2023 17:06:52 -0300 Subject: [PATCH 008/271] fix eslint issue --- .../ProductTemplates/SimpleProductTemplate.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php b/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php index caa7c46f110..baaebfe167f 100644 --- a/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php +++ b/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php @@ -375,7 +375,7 @@ class SimpleProductTemplate extends AbstractProductFormTemplate implements Produ 'id' => 'product-catalog-section', 'order' => 10, 'attributes' => array( - 'title' => __( 'Product catalog', 'woocommerce' ), + 'title' => __( 'Product catalog', 'woocommerce' ), 'description' => __( 'Help customers find this product by assigning it to categories, adding extra details, and managing its visibility in your store and other channels.', 'woocommerce' ), ), ) @@ -453,7 +453,7 @@ class SimpleProductTemplate extends AbstractProductFormTemplate implements Produ 'id' => 'product-attributes-section', 'order' => 20, 'attributes' => array( - 'title' => __( 'Attributes', 'woocommerce' ), + 'title' => __( 'Attributes', 'woocommerce' ), 'description' => __( 'Add descriptive pieces of information that customers can use to filter and search for this product. Learn more.', 'woocommerce' ), ), ) From 74e27e6f2640a972b4ee60f757b4fc6b939eb2d1 Mon Sep 17 00:00:00 2001 From: Saggre Date: Wed, 23 Mar 2022 10:41:13 +0200 Subject: [PATCH 009/271] Remove duplicate tests. --- .../php/includes/wc-formatting-functions-test.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plugins/woocommerce/tests/php/includes/wc-formatting-functions-test.php b/plugins/woocommerce/tests/php/includes/wc-formatting-functions-test.php index 93dfda3b314..426255ffaff 100644 --- a/plugins/woocommerce/tests/php/includes/wc-formatting-functions-test.php +++ b/plugins/woocommerce/tests/php/includes/wc-formatting-functions-test.php @@ -17,4 +17,15 @@ class WC_Formatting_Functions_Test extends \WC_Unit_Test_Case { $this->assertEquals( 'DUMMYCOUPON', wc_sanitize_coupon_code( 'DUMMYCOUPON' ) ); $this->assertEquals( 'a&a', wc_sanitize_coupon_code( 'a&a' ) ); } + + /** + * Test wc_format_postcode() function. + */ + public function test_wc_format_postcode() { + // IE postcode. + $this->assertEquals( 'D02 AF30', wc_format_postcode( 'D02AF30', 'IE' ) ); + + // PT postcode. + $this->assertEquals( '1000-205', wc_format_postcode( '1000205', 'PT' ) ); + } } From cd32a5a08704587b1a65d4e08e791b9c14f630b1 Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Wed, 22 Nov 2023 15:13:06 -0800 Subject: [PATCH 010/271] Changelog. --- plugins/woocommerce/changelog/feature-postcode-format-tests | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 plugins/woocommerce/changelog/feature-postcode-format-tests diff --git a/plugins/woocommerce/changelog/feature-postcode-format-tests b/plugins/woocommerce/changelog/feature-postcode-format-tests new file mode 100644 index 00000000000..588197f1595 --- /dev/null +++ b/plugins/woocommerce/changelog/feature-postcode-format-tests @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Adds test coverage for formatting of Portugese and Irish postcodes. Since it does not add user-facing functionality, a changelog entry is not needed. + + From e3d1a3c7b9baf2bffea114041a0c8270993c42eb Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Wed, 22 Nov 2023 15:14:08 -0800 Subject: [PATCH 011/271] Convert comments to assertion messages. --- .../tests/php/includes/wc-formatting-functions-test.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/woocommerce/tests/php/includes/wc-formatting-functions-test.php b/plugins/woocommerce/tests/php/includes/wc-formatting-functions-test.php index 426255ffaff..7beda829931 100644 --- a/plugins/woocommerce/tests/php/includes/wc-formatting-functions-test.php +++ b/plugins/woocommerce/tests/php/includes/wc-formatting-functions-test.php @@ -22,10 +22,7 @@ class WC_Formatting_Functions_Test extends \WC_Unit_Test_Case { * Test wc_format_postcode() function. */ public function test_wc_format_postcode() { - // IE postcode. - $this->assertEquals( 'D02 AF30', wc_format_postcode( 'D02AF30', 'IE' ) ); - - // PT postcode. - $this->assertEquals( '1000-205', wc_format_postcode( '1000205', 'PT' ) ); + $this->assertEquals( 'D02 AF30', wc_format_postcode( 'D02AF30', 'IE' ), 'Test formatting of IE postcodes.' ); + $this->assertEquals( '1000-205', wc_format_postcode( '1000205', 'PT' ), 'Test formatting of PT postcodes.' ); } } From 595f99d9332280452f682899d026ed08e988f977 Mon Sep 17 00:00:00 2001 From: Jonathan Lane Date: Wed, 22 Nov 2023 20:41:27 -0800 Subject: [PATCH 012/271] Run daily smoke tests on wp-env (#41560) * Run daily smoke tests on wp-env * Add changelog * Add step to install playwright to e2e tests * Run k6 containers as e2e * Get build first for k6 * Tweak for API report * Change k6 tests back to non-local --------- Co-authored-by: Jon Lane --- .github/workflows/smoke-test-daily.yml | 98 ++++++++++++------- .../changelog/e2e-move-daily-tests-to-wp-env | 4 + 2 files changed, 65 insertions(+), 37 deletions(-) create mode 100644 plugins/woocommerce/changelog/e2e-move-daily-tests-to-wp-env diff --git a/.github/workflows/smoke-test-daily.yml b/.github/workflows/smoke-test-daily.yml index c1bf5937264..ca97caa9d76 100644 --- a/.github/workflows/smoke-test-daily.yml +++ b/.github/workflows/smoke-test-daily.yml @@ -27,23 +27,16 @@ jobs: env: ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-results ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-report - BASE_URL: ${{ secrets.SMOKE_TEST_URL }} - API_BASE_URL: ${{ secrets.SMOKE_TEST_API_URL }} - ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }} - ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }} - ADMIN_USER_EMAIL: ${{ secrets.SMOKE_TEST_ADMIN_USER_EMAIL }} - CUSTOMER_USER: ${{ secrets.SMOKE_TEST_CUSTOMER_USER }} - CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_CUSTOMER_PASSWORD }} - DEFAULT_TIMEOUT_OVERRIDE: 120000 - UPDATE_WC: 'nightly' steps: - uses: actions/checkout@v3 - name: Setup WooCommerce Monorepo uses: ./.github/actions/setup-woocommerce-monorepo + + - name: Setup local test environment + uses: ./.github/actions/tests/setup-local-test-environment with: - install-filters: woocommerce - build: false + test-type: api - name: Run API tests id: run-api-composite-action @@ -51,10 +44,34 @@ jobs: with: report-name: ${{ env.API_ARTIFACT }} env: - USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }} - USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }} + BASE_URL: http://localhost:8086 + USER_KEY: admin + USER_SECRET: password GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }} - UPDATE_WC: nightly + + - name: Generate Playwright API Test report. + id: generate_api_report + if: | + always() && + ( + steps.run-api-composite-action.conclusion != 'cancelled' || + steps.run-api-composite-action.conclusion != 'skipped' + ) + working-directory: plugins/woocommerce + run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }} + + - name: Archive Playwright API test report + if: | + always() && + steps.generate_api_report.conclusion == 'success' + uses: actions/upload-artifact@v3 + with: + name: ${{ env.API_ARTIFACT }} + path: | + ${{ env.ALLURE_RESULTS_DIR }} + ${{ env.ALLURE_REPORT_DIR }} + if-no-files-found: ignore + retention-days: 20 e2e-tests: name: E2E tests on nightly build @@ -62,45 +79,52 @@ jobs: permissions: contents: read outputs: - test-result: ${{ steps.run-e2e-composite-action.outputs.result }} + test-result: ${{ steps.run_playwright_e2e_tests.outputs.result }} # needs: [api-tests] env: - ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }} - ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }} - ADMIN_USER_EMAIL: ${{ secrets.SMOKE_TEST_ADMIN_USER_EMAIL }} ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-report ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-results - BASE_URL: ${{ secrets.SMOKE_TEST_URL }} - CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_CUSTOMER_PASSWORD }} - CUSTOMER_USER: ${{ secrets.SMOKE_TEST_CUSTOMER_USER }} - DEFAULT_TIMEOUT_OVERRIDE: 120000 steps: - uses: actions/checkout@v3 - name: Setup WooCommerce Monorepo uses: ./.github/actions/setup-woocommerce-monorepo + + - name: Setup local test environment + uses: ./.github/actions/tests/setup-local-test-environment with: - install-filters: woocommerce - build: false + test-type: e2e + + - name: Download and install Chromium browser. + working-directory: plugins/woocommerce + run: pnpm exec playwright install chromium - name: Run E2E tests - id: run-e2e-composite-action timeout-minutes: 60 - uses: ./.github/actions/tests/run-e2e-tests - with: - report-name: ${{ env.E2E_ARTIFACT }} + id: run_playwright_e2e_tests env: - ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }} - ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }} - ADMIN_USER_EMAIL: ${{ secrets.SMOKE_TEST_ADMIN_USER_EMAIL }} + USE_WP_ENV: 1 + E2E_MAX_FAILURES: 15 + FORCE_COLOR: 1 ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-report ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-results - BASE_URL: ${{ secrets.SMOKE_TEST_URL }} - CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_CUSTOMER_PASSWORD }} - CUSTOMER_USER: ${{ secrets.SMOKE_TEST_CUSTOMER_USER }} - DEFAULT_TIMEOUT_OVERRIDE: 120000 - E2E_MAX_FAILURES: 25 - RESET_SITE: true + working-directory: plugins/woocommerce + run: pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js + + - name: Generate Test report. + if: success() || ( failure() && steps.run_playwright_e2e_tests.conclusion == 'failure' ) + working-directory: plugins/woocommerce + run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }} + + - name: Archive test report + if: success() || ( failure() && steps.run_playwright_e2e_tests.conclusion == 'failure' ) + uses: actions/upload-artifact@v3 + with: + name: ${{ env.E2E_ARTIFACT }} + path: | + ${{ env.ALLURE_RESULTS_DIR }} + ${{ env.ALLURE_REPORT_DIR }} + retention-days: 20 k6-tests: name: k6 tests on nightly build diff --git a/plugins/woocommerce/changelog/e2e-move-daily-tests-to-wp-env b/plugins/woocommerce/changelog/e2e-move-daily-tests-to-wp-env new file mode 100644 index 00000000000..9cd821e5ba9 --- /dev/null +++ b/plugins/woocommerce/changelog/e2e-move-daily-tests-to-wp-env @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update daily smoke test to run locally instead of against an external host From 2fc7057e16ca8f0460b8a6e1468149a75fe6d098 Mon Sep 17 00:00:00 2001 From: nigeljamesstevenson <105309450+nigeljamesstevenson@users.noreply.github.com> Date: Thu, 23 Nov 2023 11:30:20 +0000 Subject: [PATCH 013/271] Update deciding-pr-high-impact.md --- docs/contributing/deciding-pr-high-impact.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/contributing/deciding-pr-high-impact.md b/docs/contributing/deciding-pr-high-impact.md index d0e283ac822..24c7598705f 100644 --- a/docs/contributing/deciding-pr-high-impact.md +++ b/docs/contributing/deciding-pr-high-impact.md @@ -30,6 +30,4 @@ On this page, we will share some guidelines to help you assess the impact degree ## My PR is High-Impact. What's next? -If your PR is High-Impact, be sure to label it with `needs: analysis` so that it can get reviewed. If you don't have permission to add labels, please make sure that the **PR reviewers do it**. - -Once the PR labelled as `needs: analysis` is reviewed, it will get added the `impact: high` label if it qualifies for High-Impact and the WooCommerce Core team will keep special considerations for testing it. +If your PR is High-Impact, be sure to label it with `impact: high` and the WooCommerce Core team will keep special considerations for testing it. From 4a0db063211fb66c42501645df0443f0037b88b1 Mon Sep 17 00:00:00 2001 From: Fernando Marichal Date: Thu, 23 Nov 2023 12:00:17 -0300 Subject: [PATCH 014/271] Modify notice for legacy local attributes (#41646) * Modify notice for legacy local attributes * Rename variable * Add changelogs * Rename local_attributes_notice_dismissed_ids --- ...8_notice_when_product_has_local_attributes | 4 ++++ packages/js/data/src/user/types.ts | 3 ++- ...8_notice_when_product_has_local_attributes | 4 ++++ .../product-fields/variation-options/edit.tsx | 19 ++++++++++++++----- ...8_notice_when_product_has_local_attributes | 4 ++++ .../Features/ProductBlockEditor/Init.php | 2 +- 6 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 packages/js/data/changelog/add-40348_notice_when_product_has_local_attributes create mode 100644 packages/js/product-editor/changelog/add-40348_notice_when_product_has_local_attributes create mode 100644 plugins/woocommerce/changelog/add-40348_notice_when_product_has_local_attributes diff --git a/packages/js/data/changelog/add-40348_notice_when_product_has_local_attributes b/packages/js/data/changelog/add-40348_notice_when_product_has_local_attributes new file mode 100644 index 00000000000..9e486f80de7 --- /dev/null +++ b/packages/js/data/changelog/add-40348_notice_when_product_has_local_attributes @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Modify notice for legacy local attributes #41646 diff --git a/packages/js/data/src/user/types.ts b/packages/js/data/src/user/types.ts index d3c40b1200b..97f11e892ac 100644 --- a/packages/js/data/src/user/types.ts +++ b/packages/js/data/src/user/types.ts @@ -28,13 +28,14 @@ export type UserPreferences = { variable_product_tour_shown?: string; variable_product_block_tour_shown?: string; variations_report_columns?: string; - product_block_variable_options_notice_dismissed?: string; + local_attributes_notice_dismissed_ids?: number[]; variable_items_without_price_notice_dismissed?: Record< number, string >; }; export type WoocommerceMeta = UserPreferences & { task_list_tracked_started_tasks?: string; variable_items_without_price_notice_dismissed?: string; + local_attributes_notice_dismissed_ids?: string; }; export type WCUser< diff --git a/packages/js/product-editor/changelog/add-40348_notice_when_product_has_local_attributes b/packages/js/product-editor/changelog/add-40348_notice_when_product_has_local_attributes new file mode 100644 index 00000000000..9e486f80de7 --- /dev/null +++ b/packages/js/product-editor/changelog/add-40348_notice_when_product_has_local_attributes @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Modify notice for legacy local attributes #41646 diff --git a/packages/js/product-editor/src/blocks/product-fields/variation-options/edit.tsx b/packages/js/product-editor/src/blocks/product-fields/variation-options/edit.tsx index 0e7f4bffe27..24b99091e1b 100644 --- a/packages/js/product-editor/src/blocks/product-fields/variation-options/edit.tsx +++ b/packages/js/product-editor/src/blocks/product-fields/variation-options/edit.tsx @@ -16,6 +16,7 @@ import { } from '@woocommerce/data'; import { recordEvent } from '@woocommerce/tracks'; import { Link } from '@woocommerce/components'; +import { getAdminLink } from '@woocommerce/settings'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore No types for this exist yet. // eslint-disable-next-line @woocommerce/dependency-group @@ -31,13 +32,13 @@ import { ProductEditorBlockEditProps } from '../../../types'; export function Edit( { attributes: blockAttributes, + context, }: ProductEditorBlockEditProps< BlockAttributes > ) { const blockProps = useWooBlockProps( blockAttributes ); const { generateProductVariations } = useProductVariationsHelper(); const { updateUserPreferences, - product_block_variable_options_notice_dismissed: - hasDismissedVariableOptionsNotice, + local_attributes_notice_dismissed_ids: dismissedNoticesIds = [], } = useUserPreferences(); const [ entityAttributes, setEntityAttributes ] = useEntityProp< @@ -51,6 +52,9 @@ export function Edit( { 'default_attributes' ); + const { postType } = context; + const productId = useEntityId( 'postType', postType ); + const { attributes, handleChange } = useProductAttributes( { allAttributes: entityAttributes, isVariationAttributes: true, @@ -68,7 +72,7 @@ export function Edit( { let notice: string | React.ReactElement = ''; if ( localAttributeNames.length > 0 && - hasDismissedVariableOptionsNotice !== 'yes' + ! dismissedNoticesIds?.includes( productId ) ) { notice = createInterpolateElement( __( @@ -87,7 +91,9 @@ export function Edit( { ), globalAttributeLink: ( @@ -122,7 +128,10 @@ export function Edit( { useRemoveConfirmationModal={ true } onNoticeDismiss={ () => updateUserPreferences( { - product_block_variable_options_notice_dismissed: 'yes', + local_attributes_notice_dismissed_ids: [ + ...dismissedNoticesIds, + productId, + ], } ) } onAddAnother={ () => { diff --git a/plugins/woocommerce/changelog/add-40348_notice_when_product_has_local_attributes b/plugins/woocommerce/changelog/add-40348_notice_when_product_has_local_attributes new file mode 100644 index 00000000000..9e486f80de7 --- /dev/null +++ b/plugins/woocommerce/changelog/add-40348_notice_when_product_has_local_attributes @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Modify notice for legacy local attributes #41646 diff --git a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php index df2816d0efe..9e7f892a80e 100644 --- a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php +++ b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php @@ -175,7 +175,7 @@ class Init { $user_data_fields, array( 'variable_product_block_tour_shown', - 'product_block_variable_options_notice_dismissed', + 'local_attributes_notice_dismissed_ids', 'variable_items_without_price_notice_dismissed', ) ); From 829ca2332663c39f6425b99e5b9a557de113c065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maikel=20David=20P=C3=A9rez=20G=C3=B3mez?= Date: Thu, 23 Nov 2023 13:24:51 -0300 Subject: [PATCH 015/271] [Grouped products] Create product-grouped feature flag (#41639) * Create product-grouped feature flag * Add support to the product editor to handle grouped product types as well * Add changelog file --- plugins/woocommerce-admin/client/typings/global.d.ts | 1 + plugins/woocommerce/changelog/add-41593 | 4 ++++ plugins/woocommerce/client/admin/config/core.json | 1 + plugins/woocommerce/client/admin/config/development.json | 1 + .../src/Admin/Features/ProductBlockEditor/Init.php | 4 ++++ 5 files changed, 11 insertions(+) create mode 100644 plugins/woocommerce/changelog/add-41593 diff --git a/plugins/woocommerce-admin/client/typings/global.d.ts b/plugins/woocommerce-admin/client/typings/global.d.ts index 7adf1899d0d..e130d2fa061 100644 --- a/plugins/woocommerce-admin/client/typings/global.d.ts +++ b/plugins/woocommerce-admin/client/typings/global.d.ts @@ -23,6 +23,7 @@ declare global { 'product-variation-management': boolean; 'product-virtual-downloadable': boolean; 'product-external-affiliate': boolean; + 'product-grouped': boolean; 'remote-inbox-notifications': boolean; 'remote-free-extensions': boolean; settings: boolean; diff --git a/plugins/woocommerce/changelog/add-41593 b/plugins/woocommerce/changelog/add-41593 new file mode 100644 index 00000000000..a6d0cbff24f --- /dev/null +++ b/plugins/woocommerce/changelog/add-41593 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add support to the product editor to handle grouped product types as well diff --git a/plugins/woocommerce/client/admin/config/core.json b/plugins/woocommerce/client/admin/config/core.json index 9b17580ab17..fc05aaa3490 100644 --- a/plugins/woocommerce/client/admin/config/core.json +++ b/plugins/woocommerce/client/admin/config/core.json @@ -22,6 +22,7 @@ "product-variation-management": true, "product-virtual-downloadable": true, "product-external-affiliate": false, + "product-grouped": false, "remote-inbox-notifications": true, "remote-free-extensions": true, "payment-gateway-suggestions": true, diff --git a/plugins/woocommerce/client/admin/config/development.json b/plugins/woocommerce/client/admin/config/development.json index a08fb3e118f..c0ac63b650b 100644 --- a/plugins/woocommerce/client/admin/config/development.json +++ b/plugins/woocommerce/client/admin/config/development.json @@ -23,6 +23,7 @@ "product-variation-management": true, "product-virtual-downloadable": true, "product-external-affiliate": true, + "product-grouped": true, "remote-inbox-notifications": true, "remote-free-extensions": true, "settings": false, diff --git a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php index 9e7f892a80e..4a57f3033c4 100644 --- a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php +++ b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php @@ -49,6 +49,10 @@ class Init { array_push( $this->supported_post_types, 'external' ); } + if ( Features::is_enabled( 'product-grouped' ) ) { + array_push( $this->supported_post_types, 'grouped' ); + } + $this->redirection_controller = new RedirectionController( $this->supported_post_types ); if ( \Automattic\WooCommerce\Utilities\FeaturesUtil::feature_is_enabled( 'product_block_editor' ) ) { From 55485012e5398554e70ebd732affd3f3e84a77f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maikel=20David=20P=C3=A9rez=20G=C3=B3mez?= Date: Thu, 23 Nov 2023 14:16:10 -0300 Subject: [PATCH 016/271] [Grouped products] Inventory tab (#41640) * The Track inventory toggle is not visible in the Inventory tab for grouped product types * Add changelog file --- plugins/woocommerce/changelog/add-35147 | 4 ++++ .../ProductTemplates/SimpleProductTemplate.php | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 plugins/woocommerce/changelog/add-35147 diff --git a/plugins/woocommerce/changelog/add-35147 b/plugins/woocommerce/changelog/add-35147 new file mode 100644 index 00000000000..e4e43aee4d9 --- /dev/null +++ b/plugins/woocommerce/changelog/add-35147 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +The Track inventory toggle is not visible in the Inventory tab for grouped product types. diff --git a/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php b/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php index baaebfe167f..e7383011f95 100644 --- a/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php +++ b/plugins/woocommerce/src/Internal/Features/ProductBlockEditor/ProductTemplates/SimpleProductTemplate.php @@ -689,9 +689,9 @@ class SimpleProductTemplate extends AbstractProductFormTemplate implements Produ '' ), ), - 'hideConditions' => Features::is_enabled( 'product-external-affiliate' ) ? array( + 'hideConditions' => Features::is_enabled( 'product-external-affiliate' ) || Features::is_enabled( 'product-grouped' ) ? array( array( - 'expression' => 'editedProduct.type === "external"', + 'expression' => 'editedProduct.type === "external" || editedProduct.type === "grouped"', ), ) : null, ) From c10f1100b3c373bf864f2bd54fe09a416350c961 Mon Sep 17 00:00:00 2001 From: Brent MacKinnon Date: Fri, 24 Nov 2023 05:03:16 -0400 Subject: [PATCH 017/271] Add plugin API callback documentation (#41588) * Create woocommerce-plugin-api-callback.md * Update woocommerce-plugin-api-callback.md * Update docs/extension-development/woocommerce-plugin-api-callback.md Co-authored-by: Leif Singer * Update docs/extension-development/woocommerce-plugin-api-callback.md Co-authored-by: Leif Singer * Update docs/extension-development/woocommerce-plugin-api-callback.md Co-authored-by: Leif Singer * Update woocommerce-plugin-api-callback.md --------- Co-authored-by: Leif Singer --- .../woocommerce-plugin-api-callback.md | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 docs/extension-development/woocommerce-plugin-api-callback.md diff --git a/docs/extension-development/woocommerce-plugin-api-callback.md b/docs/extension-development/woocommerce-plugin-api-callback.md new file mode 100644 index 00000000000..9eeebde6a8b --- /dev/null +++ b/docs/extension-development/woocommerce-plugin-api-callback.md @@ -0,0 +1,35 @@ +# WooCommerce Plugin API Callback Documentation + +## Overview + +This document provides a guide on how to use the WooCommerce Plugin API to initiate callbacks for plugin actions, especially for gateways and classes not initialized by default. + +## Callback URL Structure + +Before WooCommerce 2.0, use: + +`https://example.com/?wc-api=CALLBACK` + +In WooCommerce 2.0 or later, use the endpoint: + +`https://example.com/wc-api/CALLBACK/` + +## Behavior + +When the callback URL is accessed, WooCommerce will: + +- Initialize the `CALLBACK` class, if available +- Trigger the `woocommerce_api_callback` action +- Exit WordPress + +## Hooking into the API Callback + +To hook into the callback, add an action in your plugin: + +```php +add_action( 'woocommerce_api_callback', 'your_callback_handler_function' ); +``` + +## Redirecting After Callback + +It's possible to redirect users after the action has been executed using your custom handler function. From 597e2ed269549ce91ca726bbea456b7a2e863acb Mon Sep 17 00:00:00 2001 From: rodelgc Date: Fri, 24 Nov 2023 17:35:46 +0800 Subject: [PATCH 018/271] Update WooCommerce Core WP.org product page readme (#41660) * update readme.txt for Woo Copy update on WP.org product page * Transform to sub-header texts * Add changefile(s) from automation for the following project(s): woocommerce --------- Co-authored-by: nigeljamesstevenson Co-authored-by: github-actions --- ...660-update-woocommerce-core-wp-org-product-page-readme | 4 ++++ plugins/woocommerce/readme.txt | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 plugins/woocommerce/changelog/41660-update-woocommerce-core-wp-org-product-page-readme diff --git a/plugins/woocommerce/changelog/41660-update-woocommerce-core-wp-org-product-page-readme b/plugins/woocommerce/changelog/41660-update-woocommerce-core-wp-org-product-page-readme new file mode 100644 index 00000000000..e131712644f --- /dev/null +++ b/plugins/woocommerce/changelog/41660-update-woocommerce-core-wp-org-product-page-readme @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update WooCommerce Core WP.org product page readme. \ No newline at end of file diff --git a/plugins/woocommerce/readme.txt b/plugins/woocommerce/readme.txt index 114f3bb2c83..5bb1301d018 100644 --- a/plugins/woocommerce/readme.txt +++ b/plugins/woocommerce/readme.txt @@ -26,6 +26,14 @@ Whether you’re launching a business, taking brick-and-mortar retail online, or - **Rise to the top of search results** by leveraging [WordPress’ SEO advantage](https://www.searchenginejournal.com/wordpress-best-cms-seo/). - **Build on a platform that scales.** Get flexible eCommerce for [high-volume stores](https://woo.com/high-volume-stores/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing). += THE EASIEST WAY TO TRY WOOCOMMERCE = + +Thinking of trying WooCommerce? Woo Express is a simple, all-in-one package that includes fully managed hosting from WordPress.com, popular extensions, and world-class support from Woo — all for one monthly price. + +[Try Woo Express free for 14 days](https://woo.com/express/?utm_source=wordpress.org&utm_medium=referral&utm_campaign=wp_org_repo_listing), then get your first 3 months for just $1. + += ALL THE TOOLS YOU NEED TO SELL = + Built-in tools and popular integrations help you efficiently manage your business operations. Many services are free to add with a single click via the optional [Setup Wizard](https://woo.com/document/woocommerce-setup-wizard/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing). - **Choose how you want to get paid**. Conveniently manage payments from the comfort of your store with [WooPayments](https://woo.com/payments/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) (Available in the U.S., U.K., Ireland, Australia, New Zealand, Canada, Spain, France, Germany, and Italy). Securely accept credit cards, mobile wallets, bank transfers, and cash thanks to [100+ payment gateways](https://woo.com/product-category/woocommerce-extensions/payment-gateways/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing) – including [Stripe](https://woo.com/products/stripe/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing), [PayPal](https://woo.com/products/woocommerce-gateway-paypal-checkout/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing), and [Square](https://woo.com/products/square/?utm_medium=referral&utm_source=wordpress.org&utm_campaign=wp_org_repo_listing). From 853ba527d21e369c62f1b64fc76f973c1523d2c7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Nov 2023 18:14:04 +0800 Subject: [PATCH 019/271] Delete changelog files based on PR 41660 (#41669) Delete changelog files for 41660 Co-authored-by: WooCommerce Bot --- .../41660-update-woocommerce-core-wp-org-product-page-readme | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 plugins/woocommerce/changelog/41660-update-woocommerce-core-wp-org-product-page-readme diff --git a/plugins/woocommerce/changelog/41660-update-woocommerce-core-wp-org-product-page-readme b/plugins/woocommerce/changelog/41660-update-woocommerce-core-wp-org-product-page-readme deleted file mode 100644 index e131712644f..00000000000 --- a/plugins/woocommerce/changelog/41660-update-woocommerce-core-wp-org-product-page-readme +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Update WooCommerce Core WP.org product page readme. \ No newline at end of file From b6657403f83e5e7e058542d5100a6edac1a2d804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alba=20Rinc=C3=B3n?= Date: Fri, 24 Nov 2023 14:40:37 +0100 Subject: [PATCH 020/271] [Store Customization] Update the store title with an AI generated one (#41632) * Make the call to update the store title with an AI generated one * Add changelog --- .../client/customize-store/design-with-ai/services.ts | 8 ++++++++ plugins/woocommerce/changelog/update-store-name-with-ai | 4 ++++ 2 files changed, 12 insertions(+) create mode 100644 plugins/woocommerce/changelog/update-store-name-with-ai diff --git a/plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts b/plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts index beaac8bd108..3515b6c39ba 100644 --- a/plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts +++ b/plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts @@ -275,6 +275,14 @@ export const updateStorePatterns = async ( context.businessInfoDescription.descriptionText, }, } ), + apiFetch( { + path: '/wc/private/ai/store-title', + method: 'POST', + data: { + business_description: + context.businessInfoDescription.descriptionText, + }, + } ), ] ); if ( ! response.ai_content_generated ) { diff --git a/plugins/woocommerce/changelog/update-store-name-with-ai b/plugins/woocommerce/changelog/update-store-name-with-ai new file mode 100644 index 00000000000..be274c36673 --- /dev/null +++ b/plugins/woocommerce/changelog/update-store-name-with-ai @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Call to the store title endpoint to update the store title with an AI generated one From 3d6c2b637e743ce7504b9515d680bd3f38e2a10b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maikel=20David=20P=C3=A9rez=20G=C3=B3mez?= Date: Fri, 24 Nov 2023 14:54:45 -0300 Subject: [PATCH 021/271] Improved variations table header (#41550) * Add header row to the variations table and fix a11y table issues * Fix TableRowSkeleton styles * Add changelog files * Fix compilation errors * Fix linter errors * Fix unit tests * Fix downloads table --- packages/js/components/changelog/add-41048 | 4 + .../image-gallery/image-gallery-wrapper.tsx | 2 +- .../js/components/src/list-item/list-item.tsx | 19 +- .../components/src/sortable/sortable-item.tsx | 42 +-- .../js/components/src/sortable/sortable.tsx | 86 +++-- .../js/components/src/sortable/test/utils.ts | 14 +- packages/js/components/src/sortable/utils.ts | 4 +- .../js/product-editor/changelog/add-41048 | 4 + .../product-fields/attributes/editor.scss | 12 +- .../blocks/product-fields/downloads/edit.tsx | 5 +- .../product-fields/downloads/editor.scss | 34 +- .../variation-options/editor.scss | 12 +- .../attribute-list-item.scss | 14 +- .../components/variations-table/styles.scss | 111 +++---- .../table-row-skeleton/styles.scss | 10 +- .../table-row-skeleton/table-row-skeleton.tsx | 59 ++-- .../use-variations/use-variations.ts | 2 +- .../variations-table-row.tsx | 22 +- .../variations-table/variations-table.tsx | 305 +++++++++++------- 19 files changed, 404 insertions(+), 357 deletions(-) create mode 100644 packages/js/components/changelog/add-41048 create mode 100644 packages/js/product-editor/changelog/add-41048 diff --git a/packages/js/components/changelog/add-41048 b/packages/js/components/changelog/add-41048 new file mode 100644 index 00000000000..6b52c0d76b3 --- /dev/null +++ b/packages/js/components/changelog/add-41048 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Fix non nedded extra SortableItem wrapper diff --git a/packages/js/components/src/image-gallery/image-gallery-wrapper.tsx b/packages/js/components/src/image-gallery/image-gallery-wrapper.tsx index faf2df092e4..719001b2691 100644 --- a/packages/js/components/src/image-gallery/image-gallery-wrapper.tsx +++ b/packages/js/components/src/image-gallery/image-gallery-wrapper.tsx @@ -15,7 +15,7 @@ export type ImageGalleryWrapperProps = { allowDragging?: boolean; onDragStart?: DragEventHandler< HTMLDivElement >; onDragEnd?: DragEventHandler< HTMLDivElement >; - onDragOver?: DragEventHandler< HTMLLIElement >; + onDragOver?: DragEventHandler< HTMLDivElement >; updateOrderedChildren?: ( items: ImageGalleryChild[] ) => void; }; diff --git a/packages/js/components/src/list-item/list-item.tsx b/packages/js/components/src/list-item/list-item.tsx index f36c7815304..f17f6a63110 100644 --- a/packages/js/components/src/list-item/list-item.tsx +++ b/packages/js/components/src/list-item/list-item.tsx @@ -1,7 +1,6 @@ /** * External dependencies */ -import { DragEventHandler } from 'react'; import classnames from 'classnames'; import { createElement } from '@wordpress/element'; @@ -9,26 +8,30 @@ import { createElement } from '@wordpress/element'; * Internal dependencies */ import { SortableHandle } from '../sortable'; +import { SortableItem, SortableItemProps } from '../sortable/sortable-item'; -export type ListItemProps = { - children: JSX.Element | JSX.Element[] | string; - className?: string; - onDragStart?: DragEventHandler< HTMLDivElement >; - onDragEnd?: DragEventHandler< HTMLDivElement >; +export type ListItemProps = Omit< SortableItemProps, 'index' > & { + index?: number; }; export const ListItem = ( { children, className, + index = 0, onDragStart, onDragEnd, + ...props }: ListItemProps ) => { const isDraggable = onDragEnd && onDragStart; return ( -
+ { isDraggable && } { children } -
+ ); }; diff --git a/packages/js/components/src/sortable/sortable-item.tsx b/packages/js/components/src/sortable/sortable-item.tsx index e549a444fa8..4d36dcf3332 100644 --- a/packages/js/components/src/sortable/sortable-item.tsx +++ b/packages/js/components/src/sortable/sortable-item.tsx @@ -2,32 +2,22 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { DragEvent, DragEventHandler, KeyboardEvent, useEffect } from 'react'; +import React, { DragEvent, useEffect } from 'react'; import classnames from 'classnames'; -import { - cloneElement, - createElement, - useRef, - useContext, -} from '@wordpress/element'; +import { createElement, useRef, useContext } from '@wordpress/element'; import { Draggable } from '@wordpress/components'; /** * Internal dependencies */ -import { SortableChild } from './types'; import { SortableContext } from './sortable'; -export type SortableItemProps = { - id: string | number; +export type SortableItemProps = React.DetailedHTMLProps< + React.HTMLAttributes< HTMLDivElement >, + HTMLDivElement +> & { index: number; - children: SortableChild; - className: string; - onKeyDown?: ( event: KeyboardEvent< HTMLLIElement > ) => void; isDragging?: boolean; - onDragStart?: DragEventHandler< HTMLDivElement >; - onDragEnd?: DragEventHandler< HTMLDivElement >; - onDragOver?: DragEventHandler< HTMLLIElement >; isSelected?: boolean; }; @@ -35,14 +25,14 @@ export const SortableItem = ( { id, children, className, - onKeyDown, isDragging = false, isSelected = false, onDragStart = () => null, onDragEnd = () => null, - onDragOver = () => null, + role = 'listitem', + ...props }: SortableItemProps ) => { - const ref = useRef< HTMLLIElement >( null ); + const ref = useRef< HTMLDivElement >( null ); const sortableContext = useContext( SortableContext ); const handleDragStart = ( event: DragEvent< HTMLDivElement > ) => { @@ -61,16 +51,15 @@ export const SortableItem = ( { }, [ isSelected ] ); return ( -
  • event.preventDefault() } ref={ ref } tabIndex={ isSelected ? 0 : -1 } @@ -95,14 +84,11 @@ export const SortableItem = ( { onDragEnd: onDraggableEnd, } } > - { cloneElement( children, { - onDragStart: onDraggableStart, - onDragEnd: onDraggableEnd, - } ) } + { children } ); } } -
  • + ); }; diff --git a/packages/js/components/src/sortable/sortable.tsx b/packages/js/components/src/sortable/sortable.tsx index c808ea1b3e7..e364090db7e 100644 --- a/packages/js/components/src/sortable/sortable.tsx +++ b/packages/js/components/src/sortable/sortable.tsx @@ -10,8 +10,9 @@ import { useRef, useState, createContext, + cloneElement, } from '@wordpress/element'; -import { DragEvent, DragEventHandler, KeyboardEvent } from 'react'; +import { DragEvent, KeyboardEvent } from 'react'; import { speak } from '@wordpress/a11y'; import { throttle } from 'lodash'; import { v4 } from 'uuid'; @@ -29,17 +30,14 @@ import { isLastDroppable, moveIndex, } from './utils'; -import { SortableItem } from './sortable-item'; import { SortableChild } from './types'; -export type SortableProps = { - children: SortableChild | SortableChild[] | null | undefined; +export type SortableProps = React.DetailedHTMLProps< + React.HTMLAttributes< HTMLDivElement >, + HTMLDivElement +> & { isHorizontal?: boolean; - onDragEnd?: DragEventHandler< HTMLDivElement >; - onDragOver?: DragEventHandler< HTMLLIElement >; - onDragStart?: DragEventHandler< HTMLDivElement >; onOrderChange?: ( items: SortableChild[] ) => void; - className?: string; }; const THROTTLE_TIME = 16; @@ -54,8 +52,10 @@ export const Sortable = ( { onDragStart = () => null, onOrderChange = () => null, className, + role = 'listbox', + ...props }: SortableProps ) => { - const ref = useRef< HTMLOListElement >( null ); + const ref = useRef< HTMLDivElement >( null ); const [ items, setItems ] = useState< SortableChild[] >( [] ); const [ selectedIndex, setSelectedIndex ] = useState< number >( -1 ); const [ dragIndex, setDragIndex ] = useState< number | null >( null ); @@ -103,7 +103,7 @@ export const Sortable = ( { }; const handleDragOver = ( - event: DragEvent< HTMLLIElement >, + event: DragEvent< HTMLDivElement >, index: number ) => { if ( dragIndex === null ) { @@ -127,7 +127,7 @@ export const Sortable = ( { [ dragIndex ] ); - const handleKeyDown = ( event: KeyboardEvent< HTMLLIElement > ) => { + const handleKeyDown = ( event: KeyboardEvent< HTMLDivElement > ) => { const { key } = event; const isSelecting = dragIndex === null || dropIndex === null; const selectedLabel = getItemName( ref.current, selectedIndex ); @@ -228,17 +228,27 @@ export const Sortable = ( { return ( -
      { items.map( ( child, index ) => { const isDragging = index === dragIndex; - const itemClasses = classnames( { + + if ( + child.props.className && + child.props.className.indexOf( 'non-sortable-item' ) !== + -1 + ) { + return child; + } + + const itemClasses = classnames( child.props.className, { 'is-dragging-over-after': isDraggingOverAfter( index, dragIndex, @@ -256,37 +266,25 @@ export const Sortable = ( { ), } ); - if ( - child.props.className && - child.props.className.indexOf( 'non-sortable-item' ) !== - -1 - ) { - return
    1. { child }
    2. ; - } - - return ( - handleDragEnd( event ) } - onDragStart={ ( event ) => - handleDragStart( event, index ) - } - onDragOver={ ( event ) => { - event.preventDefault(); - throttledHandleDragOver( event, index ); - } } - onKeyDown={ ( event ) => handleKeyDown( event ) } - > - { child } - - ); + return cloneElement( child, { + key: child.key || index, + className: itemClasses, + id: `${ index }-${ v4() }`, + index, + isDragging, + isSelected: selectedIndex === index, + onDragEnd: handleDragEnd, + onDragStart: ( event: DragEvent< HTMLDivElement > ) => + handleDragStart( event, index ), + onDragOver: ( event: DragEvent< HTMLDivElement > ) => { + event.preventDefault(); + throttledHandleDragOver( event, index ); + }, + onKeyDown: ( event: KeyboardEvent< HTMLDivElement > ) => + handleKeyDown( event ), + } ); } ) } -
    +
    ); }; diff --git a/packages/js/components/src/sortable/test/utils.ts b/packages/js/components/src/sortable/test/utils.ts index 5e0222a6f9e..1457a0bb6aa 100644 --- a/packages/js/components/src/sortable/test/utils.ts +++ b/packages/js/components/src/sortable/test/utils.ts @@ -47,7 +47,7 @@ describe( 'isBefore', () => { } ), }, } as unknown; - expect( isBefore( event as DragEvent< HTMLLIElement > ) ).toBeTruthy(); + expect( isBefore( event as DragEvent< HTMLDivElement > ) ).toBeTruthy(); } ); it( 'should return true when the element is placed lower in the page', () => { @@ -60,7 +60,7 @@ describe( 'isBefore', () => { } ), }, } as unknown; - expect( isBefore( event as DragEvent< HTMLLIElement > ) ).toBeTruthy(); + expect( isBefore( event as DragEvent< HTMLDivElement > ) ).toBeTruthy(); } ); it( 'should return false when the cursor is more than half way down', () => { @@ -73,7 +73,7 @@ describe( 'isBefore', () => { } ), }, } as unknown; - expect( isBefore( event as DragEvent< HTMLLIElement > ) ).toBeFalsy(); + expect( isBefore( event as DragEvent< HTMLDivElement > ) ).toBeFalsy(); } ); it( 'should return false when the element is lower in the page', () => { @@ -86,7 +86,7 @@ describe( 'isBefore', () => { } ), }, } as unknown; - expect( isBefore( event as DragEvent< HTMLLIElement > ) ).toBeFalsy(); + expect( isBefore( event as DragEvent< HTMLDivElement > ) ).toBeFalsy(); } ); it( 'should return false when the cursor is over the right half of the element', () => { @@ -100,7 +100,7 @@ describe( 'isBefore', () => { }, } as unknown; expect( - isBefore( event as DragEvent< HTMLLIElement >, true ) + isBefore( event as DragEvent< HTMLDivElement >, true ) ).toBeFalsy(); } ); @@ -115,7 +115,7 @@ describe( 'isBefore', () => { }, } as unknown; expect( - isBefore( event as DragEvent< HTMLLIElement >, true ) + isBefore( event as DragEvent< HTMLDivElement >, true ) ).toBeTruthy(); } ); @@ -130,7 +130,7 @@ describe( 'isBefore', () => { }, } as unknown; expect( - isBefore( event as DragEvent< HTMLLIElement >, true ) + isBefore( event as DragEvent< HTMLDivElement >, true ) ).toBeTruthy(); } ); } ); diff --git a/packages/js/components/src/sortable/utils.ts b/packages/js/components/src/sortable/utils.ts index 93c1f66803a..a1d7fc3365b 100644 --- a/packages/js/components/src/sortable/utils.ts +++ b/packages/js/components/src/sortable/utils.ts @@ -32,7 +32,7 @@ export const moveIndex = < T >( * @return boolean */ export const isBefore = ( - event: DragEvent< HTMLLIElement >, + event: DragEvent< HTMLDivElement >, isHorizontal = false ) => { const target = event.target as HTMLElement; @@ -123,7 +123,7 @@ export const getPreviousIndex = ( currentIndex: number, itemCount: number ) => { }; export const getItemName = ( - parentNode: HTMLOListElement | null, + parentNode: HTMLDivElement | null, index: number ) => { const listItemNode = parentNode?.childNodes[ index ] as HTMLLIElement; diff --git a/packages/js/product-editor/changelog/add-41048 b/packages/js/product-editor/changelog/add-41048 new file mode 100644 index 00000000000..d3b70bfb8df --- /dev/null +++ b/packages/js/product-editor/changelog/add-41048 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add the columns header to the variations table diff --git a/packages/js/product-editor/src/blocks/product-fields/attributes/editor.scss b/packages/js/product-editor/src/blocks/product-fields/attributes/editor.scss index fe022fb79ba..dc7ce37571f 100644 --- a/packages/js/product-editor/src/blocks/product-fields/attributes/editor.scss +++ b/packages/js/product-editor/src/blocks/product-fields/attributes/editor.scss @@ -3,18 +3,10 @@ padding: 0; } - .woocommerce-list-item { - background: none; - border: none; - border-bottom: 1px solid $gray-200; - padding: 0; - grid-template-columns: 26% auto 90px; + .woocommerce-sortable__handle { + display: none; } - .woocommerce-sortable__handle { - display: none; - } - .components-button.has-icon { svg { fill: $gray-700; diff --git a/packages/js/product-editor/src/blocks/product-fields/downloads/edit.tsx b/packages/js/product-editor/src/blocks/product-fields/downloads/edit.tsx index 46fdd69c98e..22bf4a6e71f 100644 --- a/packages/js/product-editor/src/blocks/product-fields/downloads/edit.tsx +++ b/packages/js/product-editor/src/blocks/product-fields/downloads/edit.tsx @@ -288,7 +288,10 @@ export function Edit( { download.file.startsWith( 'blob' ); return ( - +
    { download.name } { download.name !== nameFromUrl && ( diff --git a/packages/js/product-editor/src/blocks/product-fields/downloads/editor.scss b/packages/js/product-editor/src/blocks/product-fields/downloads/editor.scss index e2ae7a96060..03b43fad829 100644 --- a/packages/js/product-editor/src/blocks/product-fields/downloads/editor.scss +++ b/packages/js/product-editor/src/blocks/product-fields/downloads/editor.scss @@ -24,6 +24,22 @@ $fixed-section-height: 224px; flex: 1 0 auto; } + &__table-row { + display: grid; + grid-template-columns: 1fr 88px; + padding: 0; + min-height: $grid-unit-80 + $grid-unit-05; + border: none; + + &:not(:last-child) { + border-bottom: 1px solid $gray-200; + } + + .woocommerce-sortable__handle { + display: none; + } + } + &__table-filename { display: flex; flex-direction: column; @@ -67,22 +83,4 @@ $fixed-section-height: 224px; font-weight: normal; margin: 0; } - - .woocommerce-sortable { - &__item .woocommerce-list-item { - display: grid; - grid-template-columns: auto 88px; - padding: 0; - min-height: calc($grid-unit-80 + $grid-unit-05); - border: none; - } - - &__item:not(:last-child) .woocommerce-list-item { - border-bottom: 1px solid $gray-200; - } - - &__handle { - display: none; - } - } } diff --git a/packages/js/product-editor/src/blocks/product-fields/variation-options/editor.scss b/packages/js/product-editor/src/blocks/product-fields/variation-options/editor.scss index 6a95b640805..18c919965e3 100644 --- a/packages/js/product-editor/src/blocks/product-fields/variation-options/editor.scss +++ b/packages/js/product-editor/src/blocks/product-fields/variation-options/editor.scss @@ -1,22 +1,12 @@ .wp-block-woocommerce-product-variations-options-field { .woocommerce-sortable { padding: 0; - - &__item:not(:last-child) .woocommerce-list-item { - border-bottom: 1px solid $gray-200; - } - } - - .woocommerce-list-item { - background: none; - border: none; - padding: 0; - grid-template-columns: 26% auto 90px; } .woocommerce-sortable__handle { display: none; } + .components-button.has-icon { svg { fill: $gray-700; diff --git a/packages/js/product-editor/src/components/attribute-list-item/attribute-list-item.scss b/packages/js/product-editor/src/components/attribute-list-item/attribute-list-item.scss index d66be1a2a98..52fa3163fe0 100644 --- a/packages/js/product-editor/src/components/attribute-list-item/attribute-list-item.scss +++ b/packages/js/product-editor/src/components/attribute-list-item/attribute-list-item.scss @@ -1,5 +1,4 @@ -.woocommerce-add-attribute-list-item, -.woocommerce-attribute-list-item { +.woocommerce-add-attribute-list-item { min-height: 82px; padding: 0 $gap-large; @@ -10,10 +9,15 @@ .woocommerce-attribute-list-item { display: grid; - grid-template-columns: 24px 26% auto 90px; + background: none; + border: none; + padding: 0; + grid-template-columns: 1fr 2fr 16 * $grid-unit; + min-height: 9 * $grid-unit; + padding: $grid-unit + $grid-unit-05 0; - &:last-child { - margin-bottom: -1px; + &:not(:last-child) { + border-bottom: 1px solid $gray-200; } &__actions { diff --git a/packages/js/product-editor/src/components/variations-table/styles.scss b/packages/js/product-editor/src/components/variations-table/styles.scss index da22399e554..800986ff46b 100644 --- a/packages/js/product-editor/src/components/variations-table/styles.scss +++ b/packages/js/product-editor/src/components/variations-table/styles.scss @@ -5,23 +5,56 @@ @import "./variations-filter/styles.scss"; @import "./table-row-skeleton/styles.scss"; -$table-row-height: calc($grid-unit * 9); - .woocommerce-product-variations { display: flex; flex-direction: column; position: relative; - &__header { - padding-bottom: $grid-unit-30; - display: grid; - grid-template-columns: $grid-unit-30 1fr auto; - column-gap: $grid-unit-30; - align-items: center; - border-bottom: 1px solid $gray-200; + &__table { + &-header { + .woocommerce-product-variations__table-row { + min-height: auto; - .variations-actions-menu__toogle:disabled { - cursor: not-allowed; + &:last-child { + text-transform: uppercase; + color: $gray-700; + font-weight: 500; + font-size: 11px; + line-height: $grid-unit-20; + border-bottom: 1px solid $gray-200; + } + } + } + &-body { + .woocommerce-product-variations__table-row { + &:not(:last-child) { + border-bottom: 1px solid $gray-200; + } + } + } + &-footer { + border-top: 1px solid $gray-200; + padding-top: $grid-unit-30; + } + &-row { + position: relative; + display: grid; + grid-template-columns: $grid-unit-30 3fr 1fr 1fr 2fr; + column-gap: $grid-unit-30; + align-items: center; + border: none; + min-height: 9 * $grid-unit; + padding: $grid-unit + $grid-unit-05 0; + } + + .woocommerce-sortable__handle { + display: none; + } + + .components-checkbox-control__input[type="checkbox"] { + &:not(:checked):not(:focus) { + border-color: $gray-600; + } } } @@ -40,10 +73,6 @@ $table-row-height: calc($grid-unit * 9); } } - &__table-row { - position: relative; - } - &__selection { margin-left: $gap-smallest; display: flex; @@ -61,12 +90,6 @@ $table-row-height: calc($grid-unit * 9); color: $alert-red; } } - - .components-checkbox-control__input[type="checkbox"] { - &:not(:checked):not(:focus) { - border-color: $gray-600; - } - } } &__filters { @@ -74,6 +97,7 @@ $table-row-height: calc($grid-unit * 9); flex-wrap: wrap; gap: $grid-unit; align-items: center; + grid-column: span 4; } &__loading { @@ -129,6 +153,10 @@ $table-row-height: calc($grid-unit * 9); gap: $gap-smaller; margin-right: $gap-smallest; + .variations-actions-menu__toogle:disabled { + cursor: not-allowed; + } + &--delete { &.components-button.components-menu-item__button.is-link { text-decoration: none; @@ -143,20 +171,6 @@ $table-row-height: calc($grid-unit * 9); &[aria-disabled="true"] { opacity: 1; } - - .components-spinner { - margin: 4px; - } - } - - .components-button { - &.components-dropdown-menu__toggle.has-icon svg { - fill: inherit; - } - - svg { - fill: none; - } } .components-button--visible { @@ -167,31 +181,4 @@ $table-row-height: calc($grid-unit * 9); color: $alert-red; } } - - .woocommerce-list-item { - display: grid; - grid-template-columns: $grid-unit-30 1fr auto 9 * $grid-unit 2 * $grid-unit-80; - column-gap: $grid-unit-30; - padding: 0; - min-height: $table-row-height; - border: none; - } - - .woocommerce-sortable { - margin: 0; - flex: 1 0 auto; - - &__item:not(:last-child) .woocommerce-list-item { - border-bottom: 1px solid $gray-200; - } - - &__handle { - display: none; - } - } - - &__footer { - border-top: 1px solid $gray-200; - padding-top: $grid-unit-30; - } } diff --git a/packages/js/product-editor/src/components/variations-table/table-row-skeleton/styles.scss b/packages/js/product-editor/src/components/variations-table/table-row-skeleton/styles.scss index e3bcb9143a7..8af1dc337ea 100644 --- a/packages/js/product-editor/src/components/variations-table/table-row-skeleton/styles.scss +++ b/packages/js/product-editor/src/components/variations-table/table-row-skeleton/styles.scss @@ -7,15 +7,9 @@ min-height: $grid-unit-30; } - display: grid; - grid-template-columns: 44px auto 25% 25% 88px; - padding: 0; - min-height: 9 * $grid-unit; - border: none; - align-items: center; - &__checkbox { @include skeleton(); + flex-shrink: 0; } &__attribute-option { @@ -38,14 +32,12 @@ &__edit-link { @include skeleton(); width: $grid-unit-70; - height: $grid-unit-40 + $grid-unit-05; flex-shrink: 0; } &__menu-toggle { @include skeleton(); width: $grid-unit-40 + $grid-unit-05; - height: $grid-unit-40 + $grid-unit-05; flex-shrink: 0; } } diff --git a/packages/js/product-editor/src/components/variations-table/table-row-skeleton/table-row-skeleton.tsx b/packages/js/product-editor/src/components/variations-table/table-row-skeleton/table-row-skeleton.tsx index e6b8e8fd3a9..0281bddfb58 100644 --- a/packages/js/product-editor/src/components/variations-table/table-row-skeleton/table-row-skeleton.tsx +++ b/packages/js/product-editor/src/components/variations-table/table-row-skeleton/table-row-skeleton.tsx @@ -5,42 +5,43 @@ import { createElement } from '@wordpress/element'; export function TableRowSkeleton() { return ( -