Merge branch 'trunk' into hpos/unit-tests

This commit is contained in:
Vedanshu Jain 2023-03-10 15:21:57 +05:30 committed by GitHub
commit f497146de9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
921 changed files with 20082 additions and 10790 deletions

View File

@ -1,27 +1,46 @@
codecov: codecov:
notify: notify:
require_ci_to_pass: yes require_ci_to_pass: yes
ignore:
- '**/tests'
- '**/test'
- 'tools/**'
- 'packages/js/admin-e2e-tests'
- 'packages/js/api-core-tests'
- 'packages/js/create-woo-extension'
- 'packages/js/e2e-core-tests'
- 'packages/js/e2e-environment'
- 'packages/js/e2e-utils'
- 'packages/js/eslint-plugin'
- 'packages/js/internal-e2e-builds'
- 'packages/js/internal-js-tests'
- 'packages/js/internal-style-build'
- '**/*.test.*'
coverage: coverage:
precision: 2 precision: 1
round: nearest round: nearest
range: "50...100" range: '50...80'
status:
project:
default:
informational: true
patch:
default:
informational: true
changes: off
status:
project:
default:
target: auto
patch:
default:
target: auto
parsers: parsers:
gcov: gcov:
branch_detection: branch_detection:
conditional: yes conditional: yes
loop: yes loop: yes
method: no method: no
macro: no macro: no
comment: false comment:
layout: 'reach, diff, flags, files'
behavior: default
require_changes: false
require_base: no
require_head: yes

View File

@ -14,15 +14,13 @@
Closes # . Closes # .
<!-- The next section is mandatory. If your PR doesn't require testing, please indicate that you are purposefully omitting instructions. -->
- [ ] This PR is a very minor change/addition and does not require testing instructions (if checked you can ignore/remove the next section).
<!-- Begin testing instructions --> <!-- Begin testing instructions -->
### How to test the changes in this Pull Request: ### How to test the changes in this Pull Request:
<!-- Otherwise, please include detailed instructions on how these changes can be tested (including pre-conditions, configuration, steps to take and expected results). It may help to write your instructions using pseudocode -- as if you're telling a computer how to execute the test. --> <!-- Please include detailed instructions on how these changes can be tested, make sure to review and follow the guide for writing high-quality testing instructions below. -->
- [ ] Have you followed the [Writing high-quality testing instructions guide](https://github.com/woocommerce/woocommerce/wiki/Writing-high-quality-testing-instructions)?
1. 1.
2. 2.
@ -35,6 +33,7 @@ Closes # .
- [ ] Have you added an explanation of what your changes do and why you'd like us to include them? - [ ] Have you added an explanation of what your changes do and why you'd like us to include them?
- [ ] Have you written new tests for your changes, as applicable? - [ ] Have you written new tests for your changes, as applicable?
- [ ] Have you created a changelog file for each project being changed, ie `pnpm --filter=<project> changelog add`? - [ ] Have you created a changelog file for each project being changed, ie `pnpm --filter=<project> changelog add`?
- [ ] Have you included testing instructions?
<!-- Mark completed items with an [x] --> <!-- Mark completed items with an [x] -->

View File

@ -61,7 +61,7 @@
'plugin: woocommerce': 'plugin: woocommerce':
- plugins/woocommerce/**/* - plugins/woocommerce/**/*
'focus: react admin': 'focus: react admin [team:Ghidorah]':
- plugins/woocommerce/src/Admin/**/* - plugins/woocommerce/src/Admin/**/*
- plugins/woocommerce/src/Internal/Admin/**/* - plugins/woocommerce/src/Internal/Admin/**/*
- plugins/woocommerce-admin/**/* - plugins/woocommerce-admin/**/*

View File

@ -1,12 +0,0 @@
# Duplicate workflow that returns success for this check when there is no relevant file change. See https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
name: Run tests against PR
on:
pull_request:
paths:
- '!**'
- '**/changelog/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: 'echo "No build required"'

View File

@ -13,6 +13,7 @@ permissions: {}
jobs: jobs:
e2e-tests-run: e2e-tests-run:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }}
name: Runs E2E tests. name: Runs E2E tests.
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
permissions: permissions:

View File

@ -1,12 +0,0 @@
# Duplicate workflow that returns success for this check when there is no relevant file change. See https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
name: Build Live Branch
on:
pull_request:
paths:
- '!**'
- '**/changelog/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: 'echo "No build required"'

View File

@ -13,7 +13,7 @@ permissions: {}
jobs: jobs:
build: build:
if: github.repository_owner == 'woocommerce' if: github.repository_owner == 'woocommerce' && github.event.pull_request.user.login != 'github-actions[bot]'
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
permissions: permissions:
contents: read contents: read

View File

@ -1,12 +0,0 @@
# Duplicate workflow that returns success for this check when there is no relevant file change. See https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
name: Run code coverage on PR
on:
pull_request:
paths:
- '!**'
- '**/changelog/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: 'echo "No build required"'

View File

@ -15,6 +15,7 @@ permissions: {}
jobs: jobs:
test: test:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }}
name: Code coverage (PHP 7.4, WP Latest) name: Code coverage (PHP 7.4, WP Latest)
timeout-minutes: 30 timeout-minutes: 30
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04

View File

@ -1,12 +0,0 @@
# Duplicate workflow that returns success for this check when there is no relevant file change. See https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
name: Run code coverage on PR
on:
pull_request:
paths:
- '!**'
- '**/changelog/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: 'echo "No build required"'

View File

@ -16,6 +16,7 @@ permissions: {}
jobs: jobs:
test: test:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }}
name: Code sniff (PHP 7.4, WP Latest) name: Code sniff (PHP 7.4, WP Latest)
timeout-minutes: 15 timeout-minutes: 15
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04

View File

@ -1,12 +0,0 @@
# Duplicate workflow that returns success for this check when there is no relevant file change. See https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
name: Highlight templates changes
on:
pull_request:
paths:
- '!**'
- '**/changelog/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: 'echo "No build required"'

View File

@ -8,6 +8,7 @@ permissions: {}
jobs: jobs:
analyze: analyze:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }}
name: Check pull request changes to highlight name: Check pull request changes to highlight
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
permissions: permissions:

View File

@ -11,6 +11,7 @@ permissions: {}
jobs: jobs:
changelogger_used: changelogger_used:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }}
name: Changelogger use name: Changelogger use
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
permissions: permissions:

View File

@ -12,6 +12,7 @@ permissions: {}
jobs: jobs:
lint-test-js: lint-test-js:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }}
name: Lint and Test JS name: Lint and Test JS
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
permissions: permissions:

View File

@ -1,12 +0,0 @@
# Duplicate workflow that returns success for this check when there is no relevant file change. See https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
name: Run smoke tests against pull request.
on:
pull_request:
paths:
- '!**'
- '**/changelog/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: 'echo "No build required"'

View File

@ -1,12 +0,0 @@
# Duplicate workflow that returns success for this check when there is no relevant file change. See https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
name: Lint and tests for JS packages and woocommerce-admin/client
on:
pull_request:
paths:
- '!**'
- '**/changelog/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: 'echo "No build required"'

View File

@ -1,12 +0,0 @@
# Duplicate workflow that returns success for this check when there is no relevant file change. See https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
name: Run unit tests on PR
on:
pull_request:
paths:
- '!**'
- '**/changelog/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: 'echo "No build required"'

View File

@ -14,6 +14,7 @@ permissions: {}
jobs: jobs:
test: test:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }}
name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} ${{ matrix.hpos && 'HPOS' || '' }} name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} ${{ matrix.hpos && 'HPOS' || '' }}
timeout-minutes: 30 timeout-minutes: 30
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04

View File

@ -3,7 +3,7 @@ on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
packages: packages:
description: 'Enter a specific package to release, or packages separated by commas, ie @woocommerce/components,@woocommerce/number. Leaving this input to the default "-a" will prepare to release all eligible packages.' description: 'Enter a specific package to release, or packages separated by commas, ie @woocommerce/components,@woocommerce/number. Leaving this input to the default "-a" will prepare to release all eligible packages. When releasing a package for the first time, pass the "--initialRelease" flag.'
required: false required: false
default: '-a' default: '-a'
@ -14,8 +14,8 @@ jobs:
name: Run prepare script name: Run prepare script
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
permissions: permissions:
contents: read contents: write
pull-requests: write pull-requests: write
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

View File

@ -2,6 +2,10 @@ name: 'Pull request post-merge processing'
on: on:
pull_request_target: pull_request_target:
types: [closed] types: [closed]
paths:
- 'packages/**'
- 'plugins/woocommerce/**'
- 'plugins/woocommerce-admin/**'
permissions: {} permissions: {}

View File

@ -10,8 +10,8 @@ if ( getenv( 'TIME_OVERRIDE' ) ) {
$base_dir = dirname( dirname( dirname( __DIR__ ) ) ); $base_dir = dirname( dirname( dirname( __DIR__ ) ) );
// The release date is 26 days after the code freeze. // The release date is 22 days after the code freeze.
$release_time = strtotime( '+26 days', $now ); $release_time = strtotime( '+22 days', $now );
$release_date = date( 'Y-m-d', $release_time ); $release_date = date( 'Y-m-d', $release_time );
$readme_file = $base_dir . '/plugins/woocommerce/readme.txt'; $readme_file = $base_dir . '/plugins/woocommerce/readme.txt';

View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
#
# Verify that the PHP version in the launched WP ENV environment is equal to expected.
#
cd $GITHUB_WORKSPACE/plugins/woocommerce
ACTUAL_PHP_VERSION=$(pnpm exec wp-env run tests-cli "wp --info | grep 'PHP version:'")
EXIT_CODE=''
echo "PHP version found in WP Env environment: \"$ACTUAL_PHP_VERSION\""
echo "Expected PHP version: \"$EXPECTED_PHP_VERSION\""
if [[ $ACTUAL_PHP_VERSION == *"$EXPECTED_PHP_VERSION"* ]]
then
EXIT_CODE=0
else
EXIT_CODE=1
fi
exit $EXIT_CODE

View File

@ -8,7 +8,6 @@ env:
API_ARTIFACT: api-daily--run-${{ github.run_number }} API_ARTIFACT: api-daily--run-${{ github.run_number }}
E2E_ARTIFACT: e2e-daily--run-${{ github.run_number }} E2E_ARTIFACT: e2e-daily--run-${{ github.run_number }}
FORCE_COLOR: 1 FORCE_COLOR: 1
BRANCH_NAME: ${{ github.ref_name }}
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}
@ -17,26 +16,23 @@ concurrency:
permissions: {} permissions: {}
jobs: jobs:
e2e-tests: api-tests:
name: E2E tests on nightly build name: API tests on nightly build
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
permissions: permissions:
contents: read contents: read
env: env:
ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }} ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-results
ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }} ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-report
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 }} BASE_URL: ${{ secrets.SMOKE_TEST_URL }}
CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_CUSTOMER_PASSWORD }} 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_USER: ${{ secrets.SMOKE_TEST_CUSTOMER_USER }}
CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_CUSTOMER_PASSWORD }}
DEFAULT_TIMEOUT_OVERRIDE: 120000 DEFAULT_TIMEOUT_OVERRIDE: 120000
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with:
ref: ${{ env.BRANCH_NAME }}
- name: Setup WooCommerce Monorepo - name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo uses: ./.github/actions/setup-woocommerce-monorepo
@ -51,10 +47,63 @@ jobs:
- name: Run 'Update WooCommerce' test. - name: Run 'Update WooCommerce' test.
working-directory: plugins/woocommerce working-directory: plugins/woocommerce
env: env:
UPDATE_WC: true UPDATE_WC: nightly
run: pnpm exec playwright test --config=tests/e2e-pw/daily.playwright.config.js update-woocommerce.spec.js run: pnpm exec playwright test --config=tests/e2e-pw/daily.playwright.config.js update-woocommerce.spec.js
- name: Run the rest of E2E tests. - name: Run API tests.
working-directory: plugins/woocommerce
env:
USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello.test.js
- name: Generate API Test report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }}
- name: Archive API test report
if: success() || failure()
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: 5
e2e-tests:
name: E2E tests on nightly build
runs-on: ubuntu-20.04
permissions:
contents: read
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
with:
install-filters: woocommerce
build: false
- name: Download and install Chromium browser.
working-directory: plugins/woocommerce
run: pnpm exec playwright install chromium
- name: Run E2E tests.
timeout-minutes: 60 timeout-minutes: 60
working-directory: plugins/woocommerce working-directory: plugins/woocommerce
env: env:
@ -78,63 +127,15 @@ jobs:
if-no-files-found: ignore if-no-files-found: ignore
retention-days: 5 retention-days: 5
api-tests:
name: API tests on nightly build
runs-on: ubuntu-20.04
permissions:
contents: read
needs: [e2e-tests]
if: success() || failure()
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
steps:
- uses: actions/checkout@v3
with:
ref: ${{ env.BRANCH_NAME }}
- name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo
with:
install-filters: woocommerce
build: false
- name: Run API tests.
working-directory: plugins/woocommerce
env:
BASE_URL: ${{ secrets.SMOKE_TEST_URL }}
USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
DEFAULT_TIMEOUT_OVERRIDE: 120000
run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello.test.js
- name: Generate API Test report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }}
- name: Archive API test report
if: success() || failure()
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: 5
k6-tests: k6-tests:
name: k6 tests on nightly build name: k6 tests on nightly build
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
permissions: permissions:
contents: read contents: read
needs: [api-tests] needs: [api-tests]
if: success() || failure() if: success() || failure()
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with:
ref: ${{ env.BRANCH_NAME }}
- name: Setup WooCommerce Monorepo - name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo uses: ./.github/actions/setup-woocommerce-monorepo
@ -154,7 +155,7 @@ jobs:
ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_PERF_ADMIN_PASSWORD }} ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_PERF_ADMIN_PASSWORD }}
CUSTOMER_USER: ${{ secrets.SMOKE_TEST_PERF_ADMIN_USER }} CUSTOMER_USER: ${{ secrets.SMOKE_TEST_PERF_ADMIN_USER }}
CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_PERF_ADMIN_PASSWORD }} CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_PERF_ADMIN_PASSWORD }}
UPDATE_WC: true UPDATE_WC: nightly
DEFAULT_TIMEOUT_OVERRIDE: 120000 DEFAULT_TIMEOUT_OVERRIDE: 120000
run: | run: |
pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js update-woocommerce.spec.js pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js update-woocommerce.spec.js
@ -180,7 +181,8 @@ jobs:
name: Smoke tests on trunk with ${{ matrix.plugin }} plugin installed name: Smoke tests on trunk with ${{ matrix.plugin }} plugin installed
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
permissions: permissions:
contents: read contents: read
needs: [api-tests]
env: env:
USE_WP_ENV: 1 USE_WP_ENV: 1
ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/allure-results ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/allure-results
@ -204,8 +206,6 @@ jobs:
repo: 'takayukister/contact-form-7' repo: 'takayukister/contact-form-7'
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with:
ref: ${{ env.BRANCH_NAME }}
- name: Setup WooCommerce Monorepo - name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo uses: ./.github/actions/setup-woocommerce-monorepo
@ -255,8 +255,8 @@ jobs:
! github.event.pull_request.head.repo.fork ! github.event.pull_request.head.repo.fork
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
permissions: permissions:
contents: read contents: read
needs: [test-plugins, k6-tests] needs: [e2e-tests, test-plugins, k6-tests]
steps: steps:
- name: Create dirs - name: Create dirs
run: | run: |
@ -269,7 +269,6 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
path: repo path: repo
ref: ${{ env.BRANCH_NAME }}
- name: Download API test report artifact - name: Download API test report artifact
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
@ -312,7 +311,7 @@ jobs:
( success() || failure() ) && ( success() || failure() ) &&
! github.event.pull_request.head.repo.fork ! github.event.pull_request.head.repo.fork
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
needs: [test-plugins, k6-tests] needs: [e2e-tests, test-plugins, k6-tests]
env: env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }} GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
RUN_ID: ${{ github.run_id }} RUN_ID: ${{ github.run_id }}

View File

@ -1,185 +1,692 @@
name: Smoke test release name: Smoke test release
on: on:
release:
types: [released, prereleased, published]
workflow_dispatch: workflow_dispatch:
inputs: inputs:
release_id: tag:
description: 'WooCommerce Release Id' description: 'WooCommerce Release Tag'
required: true required: true
concurrency:
group: ${{ github.workflow }}-${{ github.event.release.tag_name || inputs.tag }}
cancel-in-progress: true
permissions: {} permissions: {}
env:
E2E_WP_LATEST_ARTIFACT: e2e-wp-latest--run-${{ github.run_number }}
E2E_UPDATE_WC_ARTIFACT: e2e-update-wc--run-${{ github.run_number }}
FORCE_COLOR: 1
jobs: jobs:
login-run: get-tag:
name: Daily smoke test on release. name: Get WooCommerce release tag
runs-on: ubuntu-20.04
permissions: permissions:
contents: read contents: read
runs-on: ubuntu-20.04
outputs:
tag: ${{ steps.get-tag.outputs.tag }}
created: ${{ steps.created-at.outputs.created }}
steps:
- name: Validate tag
if: ${{ github.event_name == 'workflow_dispatch' }}
env:
GH_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
run: gh release view "${{ inputs.tag }}" --repo=woocommerce/woocommerce
- name: Get tag from triggered event
id: get-tag
env:
RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
run: |
echo "Triggered event: ${{ github.event_name }}"
echo "Tag from event: $RELEASE_TAG"
echo "tag=$RELEASE_TAG" >> $GITHUB_OUTPUT
- name: Verify woocommerce.zip asset
env:
GH_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
RELEASE_TAG: ${{ steps.get-tag.outputs.tag }}
run: |
ASSET_NAMES=$(gh release view $RELEASE_TAG --repo woocommerce/woocommerce --json assets --jq ".assets[].name")
if [[ $ASSET_NAMES == *"woocommerce.zip"* ]]
then
echo "$RELEASE_TAG has a valid woocommerce.zip asset."
exit 0
fi
echo "$RELEASE_TAG does not have a valid woocommerce.zip asset."
exit 1
- name: Get 'created-at' of WooCommerce zip
id: created-at
env:
GH_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
run: echo "created=$(gh release view ${{ steps.get-tag.outputs.tag }} --json assets --jq .assets[0].createdAt --repo woocommerce/woocommerce)" >> $GITHUB_OUTPUT
e2e-update-wc:
name: Test WooCommerce update
runs-on: ubuntu-20.04
needs: [get-tag]
permissions:
contents: read
env:
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
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with:
ref: trunk
- name: Setup WooCommerce Monorepo - name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo uses: ./.github/actions/setup-woocommerce-monorepo
with: with:
build-filters: woocommerce install-filters: woocommerce
build: false
- name: Install Jest - name: Download and install Chromium browser.
run: npm install -g jest working-directory: plugins/woocommerce
run: pnpm exec playwright install chromium
- name: Run smoke test. - name: Run 'Update WooCommerce' test.
working-directory: plugins/woocommerce working-directory: plugins/woocommerce
env: env:
SMOKE_TEST_URL: ${{ secrets.RELEASE_TEST_URL }} ADMIN_PASSWORD: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }}
SMOKE_TEST_ADMIN_USER: ${{ secrets.RELEASE_TEST_ADMIN_USER }} ADMIN_USER: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
SMOKE_TEST_ADMIN_PASSWORD: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }} BASE_URL: ${{ secrets.RELEASE_TEST_URL }}
SMOKE_TEST_ADMIN_USER_EMAIL: ${{ secrets.RELEASE_TEST_ADMIN_USER_EMAIL }} CUSTOMER_PASSWORD: ${{ secrets.RELEASE_TEST_CUSTOMER_PASSWORD }}
SMOKE_TEST_CUSTOMER_USER: ${{ secrets.RELEASE_TEST_CUSTOMER_USER }} CUSTOMER_USER: ${{ secrets.RELEASE_TEST_CUSTOMER_USER }}
SMOKE_TEST_CUSTOMER_PASSWORD: ${{ secrets.RELEASE_TEST_CUSTOMER_PASSWORD }}
WC_E2E_SCREENSHOTS: 1
E2E_RETEST: 1
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: 'C02DS4NE72S'
TEST_RELEASE: 1
UPDATE_WC: 1
DEFAULT_TIMEOUT_OVERRIDE: 120000 DEFAULT_TIMEOUT_OVERRIDE: 120000
GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
UPDATE_WC: ${{ needs.get-tag.outputs.tag }}
run: |
pnpm exec playwright test \
--config=tests/e2e-pw/playwright.config.js \
update-woocommerce.spec.js
- name: Generate 'Update WooCommerce' test report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }}
- name: Configure AWS credentials
if: success() || failure()
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.REPORTS_AWS_SECRET_ACCESS_KEY }}
- name: Upload Allure files to bucket
if: success() || failure()
run: |
aws s3 sync ${{ env.ALLURE_RESULTS_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.E2E_WP_LATEST_ARTIFACT }}/allure-results \
--quiet
aws s3 sync ${{ env.ALLURE_REPORT_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.E2E_WP_LATEST_ARTIFACT }}/allure-report \
--quiet
- name: Publish E2E Allure report
if: success() || failure()
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
ENV_DESCRIPTION: wp-latest
run: |
gh workflow run publish-test-reports-release.yml \
-f created_at="${{ needs.get-tag.outputs.created }}" \
-f run_id=${{ github.run_id }} \
-f run_number=${{ github.run_number }} \
-f release_tag=${{ needs.get-tag.outputs.tag }} \
-f artifact="${{ env.E2E_WP_LATEST_ARTIFACT }}" \
-f env_description="${{ env.ENV_DESCRIPTION }}" \
-f test_type="e2e" \
--repo woocommerce/woocommerce-test-reports
- name: Archive 'Update WooCommerce' test report
if: success() || failure()
uses: actions/upload-artifact@v3
with:
name: ${{ env.E2E_UPDATE_WC_ARTIFACT }}
path: |
${{ env.ALLURE_RESULTS_DIR }}
${{ env.ALLURE_REPORT_DIR }}
if-no-files-found: ignore
retention-days: 5
api-wp-latest:
name: API on WP Latest
runs-on: ubuntu-20.04
needs: [get-tag, e2e-update-wc]
permissions:
contents: read
env:
ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/api-test-report/allure-report
ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/api-test-report/allure-results
API_WP_LATEST_ARTIFACT: api-wp-latest--run-${{ github.run_number }}
steps:
- uses: actions/checkout@v3
- name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo
with:
install-filters: woocommerce
build: false
- name: Run API tests.
working-directory: plugins/woocommerce
env:
BASE_URL: ${{ secrets.RELEASE_TEST_URL }} BASE_URL: ${{ secrets.RELEASE_TEST_URL }}
USER_KEY: ${{ secrets.RELEASE_TEST_ADMIN_USER }} USER_KEY: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
USER_SECRET: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }} USER_SECRET: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }}
run: | run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello
pnpm exec wc-e2e docker:up
pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js
pnpm exec wc-e2e test:e2e
pnpm exec wc-api-tests test api
test-wp-version:
name: Smoke test on L-${{ matrix.wp }} WordPress version
runs-on: ubuntu-20.04
permissions:
contents: read
strategy:
matrix:
wp: ['1', '2']
steps:
- name: Create dirs.
run: |
mkdir -p package/woocommerce
mkdir -p tmp/woocommerce
- uses: actions/checkout@v3 - name: Generate API Test report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }}
- name: Configure AWS credentials
if: success() || failure()
uses: aws-actions/configure-aws-credentials@v1-node16
with: with:
path: package/woocommerce aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.REPORTS_AWS_SECRET_ACCESS_KEY }}
- name: Upload Allure files to bucket
if: success() || failure()
run: |
aws s3 cp ${{ env.ALLURE_RESULTS_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.API_WP_LATEST_ARTIFACT }}/allure-results \
--recursive \
--quiet
aws s3 cp ${{ env.ALLURE_REPORT_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.API_WP_LATEST_ARTIFACT }}/allure-report \
--recursive \
--quiet
- name: Publish API Allure report
if: success() || failure()
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
ENV_DESCRIPTION: wp-latest
run: |
gh workflow run publish-test-reports-release.yml \
-f created_at="${{ needs.get-tag.outputs.created }}" \
-f run_id=${{ github.run_id }} \
-f run_number=${{ github.run_number }} \
-f release_tag=${{ needs.get-tag.outputs.tag }} \
-f artifact="${{ env.API_WP_LATEST_ARTIFACT }}" \
-f env_description="${{ env.ENV_DESCRIPTION }}" \
-f test_type="api" \
--repo woocommerce/woocommerce-test-reports
- name: Archive API test report
if: success() || failure()
uses: actions/upload-artifact@v3
with:
name: ${{ env.API_WP_LATEST_ARTIFACT }}
path: |
${{ env.ALLURE_RESULTS_DIR }}
${{ env.ALLURE_REPORT_DIR }}
if-no-files-found: ignore
retention-days: 5
e2e-wp-latest:
name: E2E on WP Latest
runs-on: ubuntu-20.04
needs: [get-tag, api-wp-latest]
permissions:
contents: read
env:
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
steps:
- uses: actions/checkout@v3
- name: Setup WooCommerce Monorepo - name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo uses: ./.github/actions/setup-woocommerce-monorepo
with: with:
build-filters: woocommerce install-filters: woocommerce
build: false
- name: Fetch Asset ID - name: Download and install Chromium browser.
id: fetch_asset_id working-directory: plugins/woocommerce
uses: actions/github-script@v5 run: pnpm exec playwright install chromium
- name: Run E2E tests
env: env:
RELEASE_ID: ${{ github.event.inputs.release_id }} ADMIN_PASSWORD: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ADMIN_USER: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
REPO: ${{ github.repository }} ADMIN_USER_EMAIL: ${{ secrets.RELEASE_TEST_ADMIN_USER_EMAIL }}
BASE_URL: ${{ secrets.RELEASE_TEST_URL }}
CUSTOMER_PASSWORD: ${{ secrets.RELEASE_TEST_CUSTOMER_PASSWORD }}
CUSTOMER_USER: ${{ secrets.RELEASE_TEST_CUSTOMER_USER }}
DEFAULT_TIMEOUT_OVERRIDE: 120000
E2E_MAX_FAILURES: 25
RESET_SITE: true
timeout-minutes: 60
working-directory: plugins/woocommerce
run: pnpm exec playwright test --config=tests/e2e-pw/ignore-plugin-tests.playwright.config.js
- name: Download 'e2e-update-wc' artifact
if: success() || failure()
uses: actions/download-artifact@v3
with: with:
script: | name: ${{ env.E2E_UPDATE_WC_ARTIFACT }}
const script = require( './package/woocommerce/.github/workflows/scripts/fetch-asset-id.js' ) path: plugins/woocommerce/tmp
await script({github, context, core})
- name: Download WooCommerce release zip - name: Add allure-results from 'e2e-update-wc'
working-directory: tmp if: success() || failure()
working-directory: plugins/woocommerce
run: cp -r tmp/allure-results tests/e2e-pw/test-results
- name: Generate E2E Test report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }}
- name: Configure AWS credentials
if: success() || failure()
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.REPORTS_AWS_SECRET_ACCESS_KEY }}
- name: Upload report to bucket
if: success() || failure()
run: | run: |
curl https://api.github.com/repos/${{ github.repository }}/releases/assets/${{ steps.fetch_asset_id.outputs.asset_id }} -LJOH 'Accept: application/octet-stream' aws s3 sync ${{ env.ALLURE_RESULTS_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.E2E_WP_LATEST_ARTIFACT }}/allure-results \
--quiet
aws s3 sync ${{ env.ALLURE_REPORT_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.E2E_WP_LATEST_ARTIFACT }}/allure-report \
--quiet
unzip woocommerce.zip -d woocommerce - name: Publish E2E Allure report
rsync -a woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/ if: success() || failure()
- name: Load docker images and start containers.
working-directory: package/woocommerce
env: env:
LATEST_WP_VERSION_MINUS: ${{ matrix.wp }} GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
run: pnpm docker:up --filter=woocommerce ENV_DESCRIPTION: wp-latest
run: |
gh workflow run publish-test-reports-release.yml \
-f created_at="${{ needs.get-tag.outputs.created }}" \
-f run_id=${{ github.run_id }} \
-f run_number=${{ github.run_number }} \
-f release_tag=${{ needs.get-tag.outputs.tag }} \
-f artifact="${{ env.E2E_WP_LATEST_ARTIFACT }}" \
-f env_description="${{ env.ENV_DESCRIPTION }}" \
-f test_type="e2e" \
--repo woocommerce/woocommerce-test-reports
- name: Run tests command. - name: Archive E2E test report
working-directory: package/woocommerce/plugins/woocommerce if: success() || failure()
env: uses: actions/upload-artifact@v3
WC_E2E_SCREENSHOTS: 1 with:
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }} name: ${{ env.E2E_WP_LATEST_ARTIFACT }}
E2E_SLACK_CHANNEL: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }} path: |
run: pnpm exec wc-e2e test:e2e ${{ env.ALLURE_RESULTS_DIR }}
${{ env.ALLURE_REPORT_DIR }}
if-no-files-found: ignore
retention-days: 5
test-plugins: get-wp-versions:
name: Smoke tests with ${{ matrix.plugin }} plugin installed name: Get WP L-1 & L-2 version numbers
needs: [get-tag]
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
permissions: permissions:
contents: read contents: read
strategy: outputs:
fail-fast: false matrix: ${{ steps.get-versions.outputs.versions }}
matrix: tag: ${{ needs.get-tag.outputs.tag }}
include: created: ${{ needs.get-tag.outputs.created }}
- plugin: 'WooCommerce Payments'
repo: 'automattic/woocommerce-payments'
- plugin: 'WooCommerce PayPal Payments'
repo: 'woocommerce/woocommerce-paypal-payments'
- plugin: 'WooCommerce Shipping & Tax'
repo: 'automattic/woocommerce-services'
- plugin: 'WooCommerce Subscriptions'
repo: WC_SUBSCRIPTIONS_REPO
private: true
- plugin: 'WordPress SEO' # Yoast SEO in the UI, but the slug is wordpress-seo
repo: 'Yoast/wordpress-seo'
- plugin: 'Contact Form 7'
repo: 'takayukister/contact-form-7'
steps: steps:
- name: Create dirs. - name: Create dirs
run: | run: |
mkdir -p package/woocommerce mkdir script
mkdir -p tmp/woocommerce mkdir repo
- uses: actions/checkout@v3 - name: Checkout
uses: actions/checkout@v3
with: with:
path: package/woocommerce path: repo
- name: Copy script to get previous WP versions
run: cp repo/plugins/woocommerce/tests/e2e-pw/utils/wordpress.js script
- name: Install axios
working-directory: script
run: npm install axios
- name: Get version numbers
id: get-versions
uses: actions/github-script@v6
with:
script: |
const { getPreviousTwoVersions } = require('./script/wordpress');
const versions = await getPreviousTwoVersions();
console.log(versions);
core.setOutput('versions', versions);
test-wp-versions:
name: Test against ${{ matrix.version.description }} (${{ matrix.version.number }})
runs-on: ubuntu-20.04
needs: [get-wp-versions]
strategy:
matrix: ${{ fromJSON(needs.get-wp-versions.outputs.matrix) }}
env:
API_ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/api/allure-report
API_ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/api/allure-results
API_WP_LATEST_X_ARTIFACT: api-${{ matrix.version.env_description }}--run-${{ github.run_number }}
E2E_ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/e2e/allure-report
E2E_ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/e2e/allure-results
E2E_WP_LATEST_X_ARTIFACT: e2e-${{ matrix.version.env_description }}--run-${{ github.run_number }}
permissions:
contents: read
steps:
- name: Checkout WooCommerce repo
uses: actions/checkout@v3
- name: Setup WooCommerce Monorepo - name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo uses: ./.github/actions/setup-woocommerce-monorepo
with:
build-filters: woocommerce
- name: Fetch Asset ID - name: Launch WP Env
id: fetch_asset_id working-directory: plugins/woocommerce
uses: actions/github-script@v5 run: pnpm run env:test
- name: Download release zip
env: env:
RELEASE_ID: ${{ github.event.inputs.release_id }} GH_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: gh release download ${{ needs.get-wp-versions.outputs.tag }} --dir tmp
REPO: ${{ github.repository }}
with:
script: |
const script = require( './package/woocommerce/.github/workflows/scripts/fetch-asset-id.js' )
await script({github, context, core})
- name: Download WooCommerce release zip - name: Replace `plugins/woocommerce` with unzipped woocommerce release build
working-directory: tmp run: unzip -d plugins -o tmp/woocommerce.zip
- name: Downgrade WordPress version to ${{ matrix.version.number }}
working-directory: plugins/woocommerce
run: | run: |
curl https://api.github.com/repos/${{ github.repository }}/releases/assets/${{ steps.fetch_asset_id.outputs.asset_id }} -LJOH 'Accept: application/octet-stream' pnpm exec wp-env run tests-cli "wp core update --version=${{ matrix.version.number }} --force"
pnpm exec wp-env run tests-cli "wp core update-db"
unzip woocommerce.zip -d woocommerce - name: Verify environment details
rsync -a woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/ working-directory: plugins/woocommerce
- name: Load docker images and start containers.
working-directory: package/woocommerce
env:
LATEST_WP_VERSION_MINUS: ${{ matrix.wp }}
run: pnpm docker:up --filter=woocommerce
- name: Run tests command.
working-directory: package/woocommerce/plugins/woocommerce
env:
WC_E2E_SCREENSHOTS: 1
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }}
PLUGIN_REPOSITORY: ${{ matrix.private && secrets[matrix.repo] || matrix.repo }}
PLUGIN_NAME: ${{ matrix.plugin }}
GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
run: | run: |
pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/upload-plugin.js pnpm exec wp-env run tests-cli "wp core version"
pnpm exec wc-e2e test:e2e pnpm exec wp-env run tests-cli "wp plugin list"
pnpm exec wp-env run tests-cli "wp theme list"
pnpm exec wp-env run tests-cli "wp user list"
- name: Run API tests.
id: api
working-directory: plugins/woocommerce
env:
ALLURE_RESULTS_DIR: ${{ env.API_ALLURE_RESULTS_DIR }}
run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello
- name: Generate API Allure report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.API_ALLURE_RESULTS_DIR }} --output ${{ env.API_ALLURE_REPORT_DIR }}
- name: Configure AWS credentials
if: success() || failure()
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.REPORTS_AWS_SECRET_ACCESS_KEY }}
- name: Upload API Allure artifacts to bucket
if: success() || failure()
run: |
aws s3 sync ${{ env.API_ALLURE_RESULTS_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.API_WP_LATEST_X_ARTIFACT }}/allure-results \
--quiet
aws s3 sync ${{ env.API_ALLURE_REPORT_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.API_WP_LATEST_X_ARTIFACT }}/allure-report \
--quiet
- name: Publish API Allure report
if: success() || failure()
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
ENV_DESCRIPTION: ${{ matrix.version.env_description }}
run: |
gh workflow run publish-test-reports-release.yml \
-f created_at="${{ needs.get-wp-versions.outputs.created }}" \
-f run_id=${{ github.run_id }} \
-f run_number=${{ github.run_number }} \
-f release_tag=${{ needs.get-wp-versions.outputs.tag }} \
-f artifact="${{ env.API_WP_LATEST_X_ARTIFACT }}" \
-f env_description="${{ env.ENV_DESCRIPTION }}" \
-f test_type="api" \
--repo woocommerce/woocommerce-test-reports
- name: Archive API Allure reports
if: success() || failure()
uses: actions/upload-artifact@v3
with:
name: ${{ env.API_WP_LATEST_X_ARTIFACT }}
path: |
${{ env.API_ALLURE_RESULTS_DIR }}
${{ env.API_ALLURE_REPORT_DIR }}
if-no-files-found: ignore
retention-days: 5
- name: Download and install Chromium browser.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec playwright install chromium
- name: Run E2E tests.
if: |
success() ||
( failure() && steps.api.conclusion == 'success' )
timeout-minutes: 60
id: e2e
env:
USE_WP_ENV: 1
E2E_MAX_FAILURES: 15
FORCE_COLOR: 1
ALLURE_RESULTS_DIR: ${{ env.E2E_ALLURE_RESULTS_DIR }}
DEFAULT_TIMEOUT_OVERRIDE: 120000
working-directory: plugins/woocommerce
run: pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js
- name: Generate E2E Allure report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.E2E_ALLURE_RESULTS_DIR }} --output ${{ env.E2E_ALLURE_REPORT_DIR }}
- name: Upload E2E Allure artifacts to bucket
if: success() || failure()
run: |
aws s3 sync ${{ env.E2E_ALLURE_RESULTS_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.E2E_WP_LATEST_X_ARTIFACT }}/allure-results \
--quiet
aws s3 sync ${{ env.E2E_ALLURE_REPORT_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.E2E_WP_LATEST_X_ARTIFACT }}/allure-report \
--quiet
- name: Archive E2E Allure reports
if: success() || failure()
uses: actions/upload-artifact@v3
with:
name: ${{ env.E2E_WP_LATEST_X_ARTIFACT }}
path: |
${{ env.E2E_ALLURE_RESULTS_DIR }}
${{ env.E2E_ALLURE_REPORT_DIR }}
if-no-files-found: ignore
retention-days: 5
- name: Publish E2E Allure report
if: success() || failure()
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
ENV_DESCRIPTION: ${{ matrix.version.env_description }}
run: |
gh workflow run publish-test-reports-release.yml \
-f created_at="${{ needs.get-wp-versions.outputs.created }}" \
-f run_id=${{ github.run_id }} \
-f run_number=${{ github.run_number }} \
-f release_tag=${{ needs.get-wp-versions.outputs.tag }} \
-f artifact="${{ env.E2E_WP_LATEST_X_ARTIFACT }}" \
-f env_description="${{ env.ENV_DESCRIPTION }}" \
-f test_type="e2e" \
--repo woocommerce/woocommerce-test-reports
test-php-versions:
name: Test against PHP ${{ matrix.php_version }}
runs-on: ubuntu-20.04
needs: [get-tag]
strategy:
matrix:
php_version: ['7.4', '8.1']
env:
API_ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-report
API_ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-results
API_ARTIFACT: api-php-${{ matrix.php_version }}--run-${{ github.run_number }}
E2E_ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-report
E2E_ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-results
E2E_ARTIFACT: e2e-php-${{ matrix.php_version }}--run-${{ github.run_number }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo
- name: Launch WP Env
working-directory: plugins/woocommerce
env:
WP_ENV_PHP_VERSION: ${{ matrix.php_version }}
run: pnpm run env:test
- name: Verify PHP version
working-directory: .github/workflows/scripts
env:
EXPECTED_PHP_VERSION: ${{ matrix.php_version }}
run: bash verify-php-version.sh
- name: Download release zip
env:
GH_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
run: gh release download ${{ needs.get-tag.outputs.tag }} --dir tmp
- name: Replace `plugins/woocommerce` with unzipped woocommerce release build
run: unzip -d plugins -o tmp/woocommerce.zip
- name: Run API tests.
id: api
working-directory: plugins/woocommerce
env:
ALLURE_RESULTS_DIR: ${{ env.API_ALLURE_RESULTS_DIR }}
run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello
- name: Generate API Allure report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.API_ALLURE_RESULTS_DIR }} --output ${{ env.API_ALLURE_REPORT_DIR }}
- name: Configure AWS credentials
if: success() || failure()
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.REPORTS_AWS_SECRET_ACCESS_KEY }}
- name: Upload API Allure artifacts to bucket
if: success() || failure()
run: |
aws s3 sync ${{ env.API_ALLURE_RESULTS_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.API_ARTIFACT }}/allure-results \
--quiet
aws s3 sync ${{ env.API_ALLURE_REPORT_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.API_ARTIFACT }}/allure-report \
--quiet
- name: Publish API Allure report
if: success() || failure()
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
ENV_DESCRIPTION: php-${{ matrix.php_version }}
run: |
gh workflow run publish-test-reports-release.yml \
-f created_at="${{ needs.get-tag.outputs.created }}" \
-f run_id=${{ github.run_id }} \
-f run_number=${{ github.run_number }} \
-f release_tag=${{ needs.get-tag.outputs.tag }} \
-f artifact="${{ env.API_ARTIFACT }}" \
-f env_description="${{ env.ENV_DESCRIPTION }}" \
-f test_type="api" \
--repo woocommerce/woocommerce-test-reports
- name: Archive API Allure reports
if: success() || failure()
uses: actions/upload-artifact@v3
with:
name: ${{ env.API_ARTIFACT }}
path: |
${{ env.API_ALLURE_RESULTS_DIR }}
${{ env.API_ALLURE_REPORT_DIR }}
if-no-files-found: ignore
retention-days: 5
- name: Download and install Chromium browser.
working-directory: plugins/woocommerce
run: pnpm exec playwright install chromium
- name: Run E2E tests.
if: |
success() ||
( failure() && steps.api.conclusion == 'success' )
timeout-minutes: 60
env:
USE_WP_ENV: 1
E2E_MAX_FAILURES: 15
FORCE_COLOR: 1
ALLURE_RESULTS_DIR: ${{ env.E2E_ALLURE_RESULTS_DIR }}
DEFAULT_TIMEOUT_OVERRIDE: 120000
working-directory: plugins/woocommerce
run: pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js
- name: Generate E2E Allure report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.E2E_ALLURE_RESULTS_DIR }} --output ${{ env.E2E_ALLURE_REPORT_DIR }}
- name: Upload E2E Allure artifacts to bucket
if: success() || failure()
run: |
aws s3 sync ${{ env.E2E_ALLURE_RESULTS_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.E2E_ARTIFACT }}/allure-results \
--quiet
aws s3 sync ${{ env.E2E_ALLURE_REPORT_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.E2E_ARTIFACT }}/allure-report \
--quiet
- name: Archive E2E Allure reports
if: success() || failure()
uses: actions/upload-artifact@v3
with:
name: ${{ env.E2E_ARTIFACT }}
path: |
${{ env.E2E_ALLURE_RESULTS_DIR }}
${{ env.E2E_ALLURE_REPORT_DIR }}
if-no-files-found: ignore
retention-days: 5
- name: Publish E2E Allure report
if: success() || failure()
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
ENV_DESCRIPTION: php-${{ matrix.php_version }}
run: |
gh workflow run publish-test-reports-release.yml \
-f created_at="${{ needs.get-tag.outputs.created }}" \
-f run_id=${{ github.run_id }} \
-f run_number=${{ github.run_number }} \
-f release_tag=${{ needs.get-tag.outputs.tag }} \
-f artifact="${{ env.E2E_ARTIFACT }}" \
-f env_description="${{ env.ENV_DESCRIPTION }}" \
-f test_type="e2e" \
--repo woocommerce/woocommerce-test-reports

View File

@ -0,0 +1,61 @@
# Duplicate workflow that returns success for this check when the author is "github-actions". See https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
name: Status Check Bypass for Automation
on:
pull_request:
jobs:
bypass-lint:
if: ${{ github.event.pull_request.user.login == 'github-actions[bot]' }}
runs-on: ubuntu-latest
name: "Lint and Test JS"
steps:
- run: 'echo "No build required"'
bypass-7-4-latest:
if: ${{ github.event.pull_request.user.login == 'github-actions[bot]' }}
runs-on: ubuntu-latest
name: "PHP 7.4 WP latest"
steps:
- run: 'echo "No build required"'
bypass-8-0-latest:
if: ${{ github.event.pull_request.user.login == 'github-actions[bot]' }}
runs-on: ubuntu-latest
name: "PHP 8.0 WP latest"
steps:
- run: 'echo "No build required"'
bypass-api-tests:
if: ${{ github.event.pull_request.user.login == 'github-actions[bot]' }}
runs-on: ubuntu-latest
name: "Runs API tests."
steps:
- run: 'echo "No build required"'
bypass-k6:
if: ${{ github.event.pull_request.user.login == 'github-actions[bot]' }}
runs-on: ubuntu-latest
name: "Runs k6 Performance tests"
steps:
- run: 'echo "No build required"'
bypass-sniff:
if: ${{ github.event.pull_request.user.login == 'github-actions[bot]' }}
runs-on: ubuntu-latest
name: "Code sniff (PHP 7.4, WP Latest)"
steps:
- run: 'echo "No build required"'
bypass-changelogger-use:
if: ${{ github.event.pull_request.user.login == 'github-actions[bot]' }}
runs-on: ubuntu-latest
name: "Changelogger use"
steps:
- run: 'echo "No build required"'
bypass-e2e:
if: ${{ github.event.pull_request.user.login == 'github-actions[bot]' }}
runs-on: ubuntu-latest
name: "Runs E2E tests."
steps:
- run: 'echo "No build required"'
bypass-pr-highlight:
if: ${{ github.event.pull_request.user.login == 'github-actions[bot]' }}
runs-on: ubuntu-latest
name: "Check pull request changes to highlight"
steps:
- run: 'echo "No build required"'

View File

@ -0,0 +1,56 @@
# Duplicate workflow that returns success for this check when there is no relevant file change. See https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks
name: Status Check Bypass for Changelog Only Changes
on:
pull_request:
paths:
- '!**'
- '**/changelog/**'
jobs:
bypass-lint:
runs-on: ubuntu-latest
name: "Lint and Test JS"
steps:
- run: 'echo "No build required"'
bypass-7-4-latest:
runs-on: ubuntu-latest
name: "PHP 7.4 WP latest"
steps:
- run: 'echo "No build required"'
bypass-8-0-latest:
runs-on: ubuntu-latest
name: "PHP 8.0 WP latest"
steps:
- run: 'echo "No build required"'
bypass-api-tests:
runs-on: ubuntu-latest
name: "Runs API tests."
steps:
- run: 'echo "No build required"'
bypass-k6:
runs-on: ubuntu-latest
name: "Runs k6 Performance tests"
steps:
- run: 'echo "No build required"'
bypass-sniff:
runs-on: ubuntu-latest
name: "Code sniff (PHP 7.4, WP Latest)"
steps:
- run: 'echo "No build required"'
bypass-changelogger-use:
runs-on: ubuntu-latest
name: "Changelogger use"
steps:
- run: 'echo "No build required"'
bypass-e2e:
runs-on: ubuntu-latest
name: "Runs E2E tests."
steps:
- run: 'echo "No build required"'
bypass-pr-highlight:
runs-on: ubuntu-latest
name: "Check pull request changes to highlight"
steps:
- run: 'echo "No build required"'

View File

@ -25,7 +25,7 @@ jobs:
node-version: 16 node-version: 16
- name: 'Install Syncpack' - name: 'Install Syncpack'
run: npm install -g syncpack@^8.2.4 run: npm install -g syncpack@^9.8.4
- name: 'List Mismatches' - name: 'List Mismatches'
run: syncpack list-mismatches run: syncpack list-mismatches

View File

@ -1,6 +1,6 @@
{ {
"dev": true, "dev": true,
"filter": "^(?:react|react-dom|typescript|@typescript-eslint|@types/react).*$", "filter": "^(?:config|react|react-dom|eslint|typescript|@typescript-eslint|@types/react|@wordpress|@types/wordpress__components|postcss).*$",
"indent": "\t", "indent": "\t",
"overrides": true, "overrides": true,
"peer": true, "peer": true,
@ -33,6 +33,15 @@
"**" "**"
] ]
}, },
{
"dependencies": [
"config"
],
"packages": [
"**"
],
"pinVersion": "3.3.7"
},
{ {
"dependencies": [ "dependencies": [
"react", "react",
@ -51,6 +60,100 @@
"**" "**"
], ],
"pinVersion": "^4.8.3" "pinVersion": "^4.8.3"
},
{
"dependencies": [
"eslint"
],
"dependencyTypes": [
"devDependencies"
],
"packages": [
"**"
],
"pinVersion": "^8.32.0"
},
{
"dependencies": [
"@wordpress/eslint-plugin",
"@wordpress/babel-plugin-import-jsx-pragma",
"@wordpress/babel-preset-default",
"@wordpress/env",
"@wordpress/stylelint-config",
"@wordpress/prettier-config",
"@wordpress/scripts",
"@wordpress/jest-console",
"@wordpress/dependency-extraction-webpack-plugin",
"@wordpress/e2e-test-utils",
"@wordpress/jest-preset-default",
"@wordpress/postcss-plugins-preset",
"@wordpress/custom-templated-path-webpack-plugin",
"@wordpress/postcss-themes"
],
"packages": [
"**"
],
"isIgnored": true
},
{
"dependencies": [
"@wordpress/block**",
"@wordpress/viewport"
],
"packages": [
"@woocommerce/product-editor",
"woocommerce/client/admin",
"@woocommerce/components"
],
"isIgnored": true
},
{
"dependencies": [
"@wordpress/**"
],
"packages": [
"@woocommerce/experimental"
],
"isIgnored": true
},
{
"dependencies": [
"@wordpress/**"
],
"packages": [
"**"
],
"pinVersion": "wp-6.0"
},
{
"dependencies": [
"@types/wordpress__components"
],
"packages": [
"**"
],
"pinVersion": "^19.10.3"
},
{
"dependencies": [
"postcss-loader"
],
"dependencyTypes": [
"devDependencies"
],
"packages": [
"**"
],
"pinVersion": "^4.3.0"
},
{
"dependencies": [
"postcss"
],
"packages": [
"**"
],
"pinVersion": "^8.4.7"
} }
] ]
} }

View File

@ -27,12 +27,13 @@ pnpm exec syncpack -- list-mismatches
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "You must sync the dependencies listed above before you can push this branch." echo "You must sync the dependencies listed above before you can push this branch."
echo "This can usually be accomplished automatically by updating the pinned version in `.syncpackrc` and then running \`pnpm run sync-dependencies\`." echo "This can usually be accomplished automatically by updating the pinned version in \`.syncpackrc\` and then running \`pnpm run sync-dependencies\`."
exit 1 exit 1
fi fi
# Ensure both branches are tracked or check-changelogger-use will fail. # Ensure both branches are tracked or check-changelogger-use will fail. Note we pass hooksPath
git checkout $PROTECTED_BRANCH --quiet # to avoid running the pre-commit hook.
git checkout $CURRENT_BRANCH --quiet git -c core.hooksPath=/dev/null checkout $PROTECTED_BRANCH --quiet
git -c core.hooksPath=/dev/null checkout $CURRENT_BRANCH --quiet
php tools/monorepo/check-changelogger-use.php $PROTECTED_BRANCH $CURRENT_BRANCH php tools/monorepo/check-changelogger-use.php $PROTECTED_BRANCH $CURRENT_BRANCH

View File

@ -1,5 +1,145 @@
== Changelog == == Changelog ==
= 7.4.1 2023-03-01 =
**WooCommerce**
* Update - Update WooCommerce Blocks to 9.4.4. [#36982](https://github.com/woocommerce/woocommerce/pull/36982)
= 7.4.0 2023-02-14 =
**WooCommerce**
* Fix - Add support for sorting by includes param. [#36215](https://github.com/woocommerce/woocommerce/pull/36215)
* Fix - Allow product tab navigation without prompting for unsaved changes [#36235](https://github.com/woocommerce/woocommerce/pull/36235)
* Fix - Convert HTML to blocks in product variation description [#36241](https://github.com/woocommerce/woocommerce/pull/36241)
* Fix - Decode HTML entities in CategoryBreadcrumbs. [#36321](https://github.com/woocommerce/woocommerce/pull/36321)
* Fix - Decode HTML entities in CategoryFieldItem. [#36367](https://github.com/woocommerce/woocommerce/pull/36367)
* Fix - Ensure order emails are responsive in most email clients, including when the current language is RTL. [#36310](https://github.com/woocommerce/woocommerce/pull/36310)
* Fix - Ensures product variation sort order is correctly persisted. [#36343](https://github.com/woocommerce/woocommerce/pull/36343)
* Fix - Ensure wc_get_order() works without arguments when HPOS is enabled. [#36496](https://github.com/woocommerce/woocommerce/pull/36496)
* Fix - Fix "Save changes?" modal saves the options after selecting the 'Discard' option [#36160](https://github.com/woocommerce/woocommerce/pull/36160)
* Fix - Fix attributes/options lists corrupt render #36236 [#36236](https://github.com/woocommerce/woocommerce/pull/36236)
* Fix - Fix bug when filtering for customer_id=0. [#36216](https://github.com/woocommerce/woocommerce/pull/36216)
* Fix - Fix deprecated usage of ${var} in strings [#36439](https://github.com/woocommerce/woocommerce/pull/36439)
* Fix - Fix edit attribute modal terms list [#36186](https://github.com/woocommerce/woocommerce/pull/36186)
* Fix - Fixes editing of child product reviews. [#35888](https://github.com/woocommerce/woocommerce/pull/35888)
* Fix - Fix for product filters when 'shop' page is the front page. [#36224](https://github.com/woocommerce/woocommerce/pull/36224)
* Fix - Fix issue where attribute term dropdown was not adhering to sort order setting. [#36047](https://github.com/woocommerce/woocommerce/pull/36047)
* Fix - Fix navigation between variations and tab selection [#36239](https://github.com/woocommerce/woocommerce/pull/36239)
* Fix - Fix notices styling in Twenty Twenty-Three [#36475](https://github.com/woocommerce/woocommerce/pull/36475)
* Fix - Fix overlapping header elements on product page [#36495](https://github.com/woocommerce/woocommerce/pull/36495)
* Fix - Fix product table dropdown issue on mobile. [#36046](https://github.com/woocommerce/woocommerce/pull/36046)
* Fix - Fix reordering list items error [#36296](https://github.com/woocommerce/woocommerce/pull/36296)
* Fix - Fix REST API order refunds enpoint when HPOS is active, and make v2 orders endpoint compatible with HPOS [#36308](https://github.com/woocommerce/woocommerce/pull/36308)
* Fix - Fix settings tables styles [#36531](https://github.com/woocommerce/woocommerce/pull/36531)
* Fix - Fix tax task showing as not completed after setting up tax [#36468](https://github.com/woocommerce/woocommerce/pull/36468)
* Fix - Fix the signature mismatch affecting wc cli commands ability to fetch user subscription data. [#36240](https://github.com/woocommerce/woocommerce/pull/36240)
* Fix - Fix total count query of orders within Analytics reports data store. [#35971](https://github.com/woocommerce/woocommerce/pull/35971)
* Fix - Hide Variations section when it is empty [#36202](https://github.com/woocommerce/woocommerce/pull/36202)
* Fix - Improve accessibility of the coupon code label, in the context of the cart page. [#36247](https://github.com/woocommerce/woocommerce/pull/36247)
* Fix - Improve the way we retrieve the alt text property for product attachments. [#35009](https://github.com/woocommerce/woocommerce/pull/35009)
* Fix - Load wc_empty_cart function for REST API calls. [#36182](https://github.com/woocommerce/woocommerce/pull/36182)
* Fix - Make HPOS UX more consistent with posts UI (so that same e2e tests passes for both). [#36282](https://github.com/woocommerce/woocommerce/pull/36282)
* Fix - Make order edit messages compatible with both posts and theorder object. [#36485](https://github.com/woocommerce/woocommerce/pull/36485)
* Fix - Make sure the tracking shortcode only operates in orders with billing information. [#33735](https://github.com/woocommerce/woocommerce/pull/33735)
* Fix - Remove persisted query on return to parent product from variation [#36365](https://github.com/woocommerce/woocommerce/pull/36365)
* Fix - Reset variation form if a new variation is given [#36078](https://github.com/woocommerce/woocommerce/pull/36078)
* Fix - Restore the pre-7.2.0 behavior for single product quantity inputs. [#36460](https://github.com/woocommerce/woocommerce/pull/36460)
* Fix - Set child orders to be children of current order parent before deleting for consistency. [#36218](https://github.com/woocommerce/woocommerce/pull/36218)
* Fix - Skip custom search for HPOS API queries as it's handled already. [#36213](https://github.com/woocommerce/woocommerce/pull/36213)
* Fix - Use Imagick functions to set parallel thread count instead of direct putenv call as suggested in https://core.trac.wordpress.org/ticket/36534#comment:129. [#35339](https://github.com/woocommerce/woocommerce/pull/35339)
* Fix - When adjusting download permissions, confirm the child products have not been removed. [#36431](https://github.com/woocommerce/woocommerce/pull/36431)
* Add - Add ability to filter variations by local attributes in REST API [#36201](https://github.com/woocommerce/woocommerce/pull/36201)
* Add - Add an admin notice about the upcoming PHP version requirement change for PHP 7.2 users [#36444](https://github.com/woocommerce/woocommerce/pull/36444)
* Add - Added a slot for extending the app with a homescreen header banner [#36467](https://github.com/woocommerce/woocommerce/pull/36467)
* Add - Added a slot for ProgressHeader and ProgressTitle component [#36482](https://github.com/woocommerce/woocommerce/pull/36482)
* Add - Add edit button to variations list items [#36079](https://github.com/woocommerce/woocommerce/pull/36079)
* Add - Added slot for tasklist completion slotfill [#36487](https://github.com/woocommerce/woocommerce/pull/36487)
* Add - Add endpoint to create all product variations [#35980](https://github.com/woocommerce/woocommerce/pull/35980)
* Add - Add exit prompt CES for users editing orders when tracking is enabled. [#35762](https://github.com/woocommerce/woocommerce/pull/35762)
* Add - Adding delayed spotlight to feedback button on current product page. [#35865](https://github.com/woocommerce/woocommerce/pull/35865)
* Add - Adding feedback button to activity bar on classic product page. [#35810](https://github.com/woocommerce/woocommerce/pull/35810)
* Add - Adding JS data store for ProductForm. [#36430](https://github.com/woocommerce/woocommerce/pull/36430)
* Add - Adding the WooProductSectionItem slot within the product editor general tab. [#36331](https://github.com/woocommerce/woocommerce/pull/36331)
* Add - Add initial product form PHP helper class to add new fields. [#36093](https://github.com/woocommerce/woocommerce/pull/36093)
* Add - Additional error logging within the CSV Exporter framework. [#34802](https://github.com/woocommerce/woocommerce/pull/34802)
* Add - Add multichannel marketing API [#36453](https://github.com/woocommerce/woocommerce/pull/36453)
* Add - Add new filter to add additional clauses for SQL statement in Variations report [#36378](https://github.com/woocommerce/woocommerce/pull/36378)
* Add - Add new product form API for extending the new Product Form MVP. [#36165](https://github.com/woocommerce/woocommerce/pull/36165)
* Add - Add Options section to new product experience form. [#35910](https://github.com/woocommerce/woocommerce/pull/35910)
* Add - Add product tour to new product management experience [#36428](https://github.com/woocommerce/woocommerce/pull/36428)
* Add - Add product variation form [#36033](https://github.com/woocommerce/woocommerce/pull/36033)
* Add - Add product variation General section [#36081](https://github.com/woocommerce/woocommerce/pull/36081)
* Add - Add product variation header actions and persistence [#36155](https://github.com/woocommerce/woocommerce/pull/36155)
* Add - Add product variation image [#36133](https://github.com/woocommerce/woocommerce/pull/36133)
* Add - Add product variation navigation component [#36076](https://github.com/woocommerce/woocommerce/pull/36076)
* Add - Add product variations flag to only show work in development [#36311](https://github.com/woocommerce/woocommerce/pull/36311)
* Add - Add product variation title to page header [#36085](https://github.com/woocommerce/woocommerce/pull/36085)
* Add - Add Product variation visibility toggle [#36020](https://github.com/woocommerce/woocommerce/pull/36020)
* Add - Add single product variation sections [#36051](https://github.com/woocommerce/woocommerce/pull/36051)
* Add - Adds support for a 'required' argument when invoking `wc_dropdown_variation_attribute_options()`. [#34579](https://github.com/woocommerce/woocommerce/pull/34579)
* Add - Add support for sorting by order metadata in HPOS queries. [#36403](https://github.com/woocommerce/woocommerce/pull/36403)
* Add - Add WooOnboardingTaskListHeader, woocommerce_admin_experimental_onboarding_tasklists filter, and woocommerce_onboarding_task_list_header Slot to task list [#36519](https://github.com/woocommerce/woocommerce/pull/36519)
* Add - Include tax options in pricing section [#36299](https://github.com/woocommerce/woocommerce/pull/36299)
* Add - Persist active tab on refresh [#36112](https://github.com/woocommerce/woocommerce/pull/36112)
* Add - Persist variations order on product save [#36109](https://github.com/woocommerce/woocommerce/pull/36109)
* Add - Product variation quantity status indicator [#35982](https://github.com/woocommerce/woocommerce/pull/35982)
* Add - Product variations card should have a fixed height. [#36053](https://github.com/woocommerce/woocommerce/pull/36053)
* Add - Remove manage_stock 'parent' value before saving the variation [#36234](https://github.com/woocommerce/woocommerce/pull/36234)
* Add - Run ces exit prompt when product import abandoned. [#35996](https://github.com/woocommerce/woocommerce/pull/35996)
* Add - Scroll newly added product attribute into view in new product management experience [#36447](https://github.com/woocommerce/woocommerce/pull/36447)
* Add - Show product CES footer on product tour close [#36516](https://github.com/woocommerce/woocommerce/pull/36516)
* Add - Truncate attribute option name to a max of 32 chars in variations list [#36134](https://github.com/woocommerce/woocommerce/pull/36134)
* Add - Trying experimental slot context with product editor fills. [#36333](https://github.com/woocommerce/woocommerce/pull/36333)
* Add - Using slotfill to insert attributes section in the product editor. [#36483](https://github.com/woocommerce/woocommerce/pull/36483)
* Add - Using slotfill to insert images section in product editor. [#36461](https://github.com/woocommerce/woocommerce/pull/36461)
* Update - Update woocommerce-blocks to 9.4.3. [#36736](https://github.com/woocommerce/woocommerce/pull/36736)
* Update - Adding WooProductFieldItem slot to product details section. [#36315](https://github.com/woocommerce/woocommerce/pull/36315)
* Update - Add permalink_template and generated_slug to products REST API response. [#36497](https://github.com/woocommerce/woocommerce/pull/36497)
* Update - Auto generate variations on option changes [#36188](https://github.com/woocommerce/woocommerce/pull/36188)
* Update - Bundled version of Action Scheduler updated to 3.5.4. [#36433](https://github.com/woocommerce/woocommerce/pull/36433)
* Update - Customers REST API endpoint will now return user metadata only when requester has an administrator role [#36408](https://github.com/woocommerce/woocommerce/pull/36408)
* Update - Disable irrelevant product tabs when variations exist [#35939](https://github.com/woocommerce/woocommerce/pull/35939)
* Update - Migrate shipping section in product editor to slot fill. [#36534](https://github.com/woocommerce/woocommerce/pull/36534)
* Update - Move product management feature flag down to experimental. [#36552](https://github.com/woocommerce/woocommerce/pull/36552)
* Update - Reimplementing product details fields in product editor as slot fills. [#36368](https://github.com/woocommerce/woocommerce/pull/36368)
* Update - Update api-core-tests readme to include a guide for writing tests [#35978](https://github.com/woocommerce/woocommerce/pull/35978)
* Update - Update store-details test snapshot to reflect updated select-control [#35808](https://github.com/woocommerce/woocommerce/pull/35808)
* Update - Update WooCommerce Blocks to 9.4.0 [#36524](https://github.com/woocommerce/woocommerce/pull/36524)
* Update - Update WooCommerce Blocks to 9.4.1 [#36553](https://github.com/woocommerce/woocommerce/pull/36553)
* Update - Update WooCommerce Blocks to 9.4.2 [#36624](https://github.com/woocommerce/woocommerce/pull/36624)
* Dev - Add advanced setting option [#36380](https://github.com/woocommerce/woocommerce/pull/36380)
* Dev - Add experimental SlotFill for task list footer [#36527](https://github.com/woocommerce/woocommerce/pull/36527)
* Dev - Cleanup product task experiment [#35950](https://github.com/woocommerce/woocommerce/pull/35950)
* Dev - Consistent folder structure for E2E and API test results [#35907](https://github.com/woocommerce/woocommerce/pull/35907)
* Dev - Fix docblock type annotations for $meta_value. [#33853](https://github.com/woocommerce/woocommerce/pull/33853)
* Dev - Fix flakiness of the `can save industry changes when navigating back to "Store Details"` E2E test. [#36260](https://github.com/woocommerce/woocommerce/pull/36260)
* Dev - Make shopper tests passable on daily smoke test site. [#35873](https://github.com/woocommerce/woocommerce/pull/35873)
* Dev - Move product attribute fetching logic into a separate hook [#36354](https://github.com/woocommerce/woocommerce/pull/36354)
* Dev - Update TaskLists::add_task() to reflect changes in TaskList::add_task() [#36104](https://github.com/woocommerce/woocommerce/pull/36104)
* Dev - Update the browserslist config for legacy client JS to match Wordpress. [#36264](https://github.com/woocommerce/woocommerce/pull/36264)
* Dev - Upgrade PHPUnit to v8 [#36273](https://github.com/woocommerce/woocommerce/pull/36273)
* Tweak - Corrects a typo in the i18n/states.php file, relating to our list of Iranian states. [#36457](https://github.com/woocommerce/woocommerce/pull/36457)
* Tweak - Derive product type from product attributes [#36243](https://github.com/woocommerce/woocommerce/pull/36243)
* Tweak - Fix typo in a function comment. [#36122](https://github.com/woocommerce/woocommerce/pull/36122)
* Tweak - Fix units in function doc comment [#36353](https://github.com/woocommerce/woocommerce/pull/36353)
* Tweak - Make related products check more robust against wrong transients. [#34742](https://github.com/woocommerce/woocommerce/pull/34742)
* Tweak - Makes it possible to use an `add_meta_boxes_<SCREEN_ID>` style hook in the HPOS editor, for parity with the traditional post editor. [#35999](https://github.com/woocommerce/woocommerce/pull/35999)
* Tweak - Minor adjustments to the ProductForm API [#36414](https://github.com/woocommerce/woocommerce/pull/36414)
* Tweak - Redirect to new product experience when in experiment group [#36381](https://github.com/woocommerce/woocommerce/pull/36381)
* Tweak - Refactor AttributeField into sub-components. [#35997](https://github.com/woocommerce/woocommerce/pull/35997)
* Tweak - Update product links when new product management experience is enabled [#36382](https://github.com/woocommerce/woocommerce/pull/36382)
* Tweak - Updates and improves the docblocks for methods WC_Order::get_total() and WC_Order::get_subtotal(). [#34385](https://github.com/woocommerce/woocommerce/pull/34385)
* Tweak - Validation of Norweigan postcodes has been added. [#36277](https://github.com/woocommerce/woocommerce/pull/36277)
* Performance - Speed up HPOS search query by using group by instead of distinct. [#35897](https://github.com/woocommerce/woocommerce/pull/35897)
* Enhancement - Add context to countries shipping to prefix [#36254](https://github.com/woocommerce/woocommerce/pull/36254)
* Enhancement - Adds new order status filters for bacs and cheque email instructions. [#35849](https://github.com/woocommerce/woocommerce/pull/35849)
* Enhancement - Improves handling of the single product page quantity selector, in relation to variable products. [#36087](https://github.com/woocommerce/woocommerce/pull/36087)
* Enhancement - Remove default WooCommerce button styles if using a block theme which adds button styles in theme.json [#36225](https://github.com/woocommerce/woocommerce/pull/36225)
= 7.3.0 2023-01-10 = = 7.3.0 2023-01-10 =
**WooCommerce** **WooCommerce**

4
changelog/pr-36705 Normal file
View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Added `woocommerce_widget_layered_nav_filters_start/end` hooks around layered nav filters widget

View File

@ -29,7 +29,7 @@
"@babel/runtime": "^7.17.2", "@babel/runtime": "^7.17.2",
"@types/node": "14.14.33", "@types/node": "14.14.33",
"@woocommerce/eslint-plugin": "workspace:*", "@woocommerce/eslint-plugin": "workspace:*",
"@wordpress/data": "^6.15.0", "@wordpress/data": "wp-6.0",
"@wordpress/eslint-plugin": "^11.1.0", "@wordpress/eslint-plugin": "^11.1.0",
"@wordpress/prettier-config": "^1.1.1", "@wordpress/prettier-config": "^1.1.1",
"babel-loader": "^8.2.3", "babel-loader": "^8.2.3",
@ -44,14 +44,14 @@
"mkdirp": "^1.0.4", "mkdirp": "^1.0.4",
"moment": "^2.29.1", "moment": "^2.29.1",
"node-stream-zip": "^1.15.0", "node-stream-zip": "^1.15.0",
"postcss-loader": "^3.0.0", "postcss-loader": "^4.3.0",
"prettier": "npm:wp-prettier@^2.2.1-beta-1", "prettier": "npm:wp-prettier@^2.2.1-beta-1",
"regenerator-runtime": "^0.13.9", "regenerator-runtime": "^0.13.9",
"request": "^2.88.2", "request": "^2.88.2",
"sass": "^1.49.9", "sass": "^1.49.9",
"sass-loader": "^10.2.1", "sass-loader": "^10.2.1",
"syncpack": "^8.3.9", "syncpack": "^9.8.4",
"turbo": "^1.4.5", "turbo": "^1.7.0",
"typescript": "^4.8.3", "typescript": "^4.8.3",
"url-loader": "^1.1.2", "url-loader": "^1.1.2",
"webpack": "^5.70.0" "webpack": "^5.70.0"
@ -62,5 +62,11 @@
"@wordpress/babel-preset-default": "^6.4.1", "@wordpress/babel-preset-default": "^6.4.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"wp-textdomain": "1.0.1" "wp-textdomain": "1.0.1"
},
"pnpm": {
"overrides": {
"@types/react": "^17.0.2",
"react": "^17.0.2"
}
} }
} }

View File

@ -62,4 +62,12 @@ To create a new package, add a new folder to `/packages`, containing…
- Usage example - Usage example
4. A `src` directory for the source of your module, which will be built by default using the `pnpm run turbo:build` command. Note that you'll want an `index.js` file that exports the package contents, see other packages for examples. 4. A `src` directory for the source of your module, which will be built by default using the `pnpm run turbo:build` command. Note that you'll want an `index.js` file that exports the package contents, see other packages for examples.
5. Add the new package name to `packages/js/dependency-extraction-webpack-plugin/assets/packages.js` so that users of that plugin will also be able to use the new package without enqueuing it. 5. A blank Changelog file, `changelog.md`.
```
# Changelog
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
```
6. Add the new package name to `packages/js/dependency-extraction-webpack-plugin/assets/packages.js` so that users of that plugin will also be able to use the new package without enqueuing it.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Use syncpack to update dependencies.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Lint fixes

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Unify semver range for `config@3.3.7` (from `^3.3.7`). Fix `node_env_var_name is not defined` error.

View File

@ -29,7 +29,7 @@
"@jest/globals": "^27.5.1", "@jest/globals": "^27.5.1",
"@types/jest": "^27.4.1", "@types/jest": "^27.4.1",
"@woocommerce/e2e-utils": "workspace:*", "@woocommerce/e2e-utils": "workspace:*",
"config": "^3.3.7" "config": "3.3.7"
}, },
"peerDependencies": { "peerDependencies": {
"@woocommerce/e2e-environment": "^0.2.3 || ^0.3.0", "@woocommerce/e2e-environment": "^0.2.3 || ^0.3.0",
@ -41,10 +41,10 @@
"@types/config": "0.0.41", "@types/config": "0.0.41",
"@types/expect-puppeteer": "^4.4.7", "@types/expect-puppeteer": "^4.4.7",
"@types/puppeteer": "^5.4.5", "@types/puppeteer": "^5.4.5",
"@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/eslint-plugin": "^5.54.0",
"@woocommerce/api": "^0.2.0", "@woocommerce/api": "^0.2.0",
"@woocommerce/eslint-plugin": "workspace:*", "@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0", "eslint": "^8.32.0",
"jest": "^27.5.1", "jest": "^27.5.1",
"jest-cli": "^27.5.1", "jest-cli": "^27.5.1",
"jest-mock-extended": "^1.0.18", "jest-mock-extended": "^1.0.18",

View File

@ -1,7 +1,7 @@
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { waitForElementByText, getElementByText } from '../utils/actions'; import { waitForElementByText } from '../utils/actions';
import { BasePage } from './BasePage'; import { BasePage } from './BasePage';
type PaymentMethodWithSetupButton = type PaymentMethodWithSetupButton =
@ -12,7 +12,7 @@ type PaymentMethodWithSetupButton =
| 'mollie' | 'mollie'
| 'bacs'; | 'bacs';
type PaymentMethod = PaymentMethodWithSetupButton | 'cod'; // type PaymentMethod = PaymentMethodWithSetupButton | 'cod';
export class PaymentsSetup extends BasePage { export class PaymentsSetup extends BasePage {
url = 'wp-admin/admin.php?page=wc-admin&task=payments'; url = 'wp-admin/admin.php?page=wc-admin&task=payments';

View File

@ -36,7 +36,7 @@
}, },
"devDependencies": { "devDependencies": {
"@woocommerce/eslint-plugin": "workspace:*", "@woocommerce/eslint-plugin": "workspace:*",
"eslint": "^8.12.0" "eslint": "^8.32.0"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Syncpack update of @typescript/eslint dependencies.

View File

@ -51,11 +51,11 @@
"@types/create-hmac": "1.1.0", "@types/create-hmac": "1.1.0",
"@types/jest": "^27.4.1", "@types/jest": "^27.4.1",
"@types/node": "13.13.5", "@types/node": "13.13.5",
"@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/eslint-plugin": "^5.54.0",
"@typescript-eslint/parser": "^5.43.0", "@typescript-eslint/parser": "^5.54.0",
"@woocommerce/eslint-plugin": "workspace:*", "@woocommerce/eslint-plugin": "workspace:*",
"axios-mock-adapter": "^1.20.0", "axios-mock-adapter": "^1.20.0",
"eslint": "^8.2.0", "eslint": "^8.32.0",
"jest": "^27", "jest": "^27",
"ts-jest": "^27", "ts-jest": "^27",
"typescript": "^4.8.3" "typescript": "^4.8.3"

View File

@ -1,6 +1,7 @@
module.exports = { module.exports = {
extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ], extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
root: true, root: true,
ignorePatterns: [ '**/test/*.ts', '**/test/*.tsx' ],
overrides: [ overrides: [
{ {
files: [ files: [

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Add a11y support for the Tree component

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Add custom rendering logic to the item label

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Add TreeControl expand/collapse functionality.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Add highlighter to the tree control

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Add selection logic to tree control component

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Added LearnMore option as well as made it possible to use this button multiple instances on the page

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Move experimental product section components to @woocommerce/product-editor package.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Fix dependency versions

View File

@ -1,4 +0,0 @@
Significance: minor
Type: tweak
Update spelling of Cancelled to Canceled for US English.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update eslint to 8.32.0 across the monorepo.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Sync @wordpress package versions via syncpack.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Adjust eslintrc for changes to eslint plugin.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: fix
Refactor createOrderedChildren

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Fix issue were Options tab was not showing up anymore in new product management screen.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Fix SelectControl and TreeControl styles.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Updating the product editor fill components to support multiple targets.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Export TreeSelectControl component and add additional props: onInputChange, alwaysShowPlaceholder, includeParent.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Add deprecated message to product slot fill components

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Updating WooProductFieldItem to uniquely generate IDs with different sections.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Add deprecated message to packages moved to product-editor package.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: tweak
Small tweak to update reference to currencyContext component.

View File

@ -37,33 +37,34 @@
"@types/wordpress__block-library": "^2.6.1", "@types/wordpress__block-library": "^2.6.1",
"@types/wordpress__blocks": "^11.0.7", "@types/wordpress__blocks": "^11.0.7",
"@types/wordpress__rich-text": "^3.4.6", "@types/wordpress__rich-text": "^3.4.6",
"@types/wordpress__components": "^19.10.3",
"@woocommerce/csv-export": "workspace:*", "@woocommerce/csv-export": "workspace:*",
"@woocommerce/currency": "workspace:*", "@woocommerce/currency": "workspace:*",
"@woocommerce/data": "workspace:*", "@woocommerce/data": "workspace:*",
"@woocommerce/date": "workspace:*", "@woocommerce/date": "workspace:*",
"@woocommerce/navigation": "workspace:*", "@woocommerce/navigation": "workspace:*",
"@wordpress/a11y": "3.5.0", "@wordpress/a11y": "wp-6.0",
"@wordpress/api-fetch": "^6.0.1", "@wordpress/api-fetch": "wp-6.0",
"@wordpress/base-styles": "^4.3.0", "@wordpress/base-styles": "wp-6.0",
"@wordpress/block-editor": "^9.8.0", "@wordpress/block-editor": "^9.8.0",
"@wordpress/block-library": "^7.16.0", "@wordpress/block-library": "^7.16.0",
"@wordpress/blocks": "^11.18.0", "@wordpress/blocks": "^11.18.0",
"@wordpress/components": "^19.5.0", "@wordpress/components": "wp-6.0",
"@wordpress/compose": "^5.1.2", "@wordpress/compose": "wp-6.0",
"@wordpress/core-data": "^4.2.1", "@wordpress/core-data": "wp-6.0",
"@wordpress/date": "^4.3.1", "@wordpress/date": "wp-6.0",
"@wordpress/deprecated": "^3.3.1", "@wordpress/deprecated": "wp-6.0",
"@wordpress/dom": "^3.3.2", "@wordpress/dom": "wp-6.0",
"@wordpress/element": "^4.1.1", "@wordpress/element": "wp-6.0",
"@wordpress/hooks": "^3.5.0", "@wordpress/hooks": "wp-6.0",
"@wordpress/html-entities": "^3.3.1", "@wordpress/html-entities": "wp-6.0",
"@wordpress/i18n": "^4.3.1", "@wordpress/i18n": "wp-6.0",
"@wordpress/icons": "^8.1.0", "@wordpress/icons": "wp-6.0",
"@wordpress/keyboard-shortcuts": "^3.17.0", "@wordpress/keyboard-shortcuts": "wp-6.0",
"@wordpress/keycodes": "^3.3.1", "@wordpress/keycodes": "wp-6.0",
"@wordpress/media-utils": "^4.6.0", "@wordpress/media-utils": "wp-6.0",
"@wordpress/rich-text": "^5.17.0", "@wordpress/rich-text": "wp-6.0",
"@wordpress/url": "^3.4.1", "@wordpress/url": "wp-6.0",
"@wordpress/viewport": "^4.1.2", "@wordpress/viewport": "^4.1.2",
"classnames": "^2.3.1", "classnames": "^2.3.1",
"core-js": "^3.21.1", "core-js": "^3.21.1",
@ -86,8 +87,10 @@
"react-transition-group": "^4.4.2" "react-transition-group": "^4.4.2"
}, },
"peerDependencies": { "peerDependencies": {
"@wordpress/data": "^6.2.1", "@wordpress/data": "wp-6.0",
"lodash": "^4.17.0", "lodash": "^4.17.0",
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.2",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2" "react-dom": "^17.0.2"
}, },
@ -117,23 +120,23 @@
"@types/jest": "^27.4.1", "@types/jest": "^27.4.1",
"@types/lodash": "^4.14.184", "@types/lodash": "^4.14.184",
"@types/prop-types": "^15.7.4", "@types/prop-types": "^15.7.4",
"@types/react": "^17.0.2",
"@types/testing-library__jest-dom": "^5.14.3", "@types/testing-library__jest-dom": "^5.14.3",
"@types/uuid": "^8.3.0", "@types/uuid": "^8.3.0",
"@types/wordpress__components": "^19.10.1", "@types/wordpress__components": "^19.10.3",
"@types/wordpress__data": "^6.0.0", "@types/wordpress__data": "^6.0.0",
"@types/wordpress__media-utils": "^3.0.0", "@types/wordpress__media-utils": "^3.0.0",
"@types/wordpress__viewport": "^2.5.4", "@types/wordpress__viewport": "^2.5.4",
"@woocommerce/eslint-plugin": "workspace:*", "@woocommerce/eslint-plugin": "workspace:*",
"@woocommerce/internal-style-build": "workspace:*", "@woocommerce/internal-style-build": "workspace:*",
"@wordpress/browserslist-config": "^4.1.1", "@wordpress/browserslist-config": "wp-6.0",
"@wordpress/scripts": "^12.6.1", "@wordpress/scripts": "^12.6.1",
"concurrently": "^7.0.0", "concurrently": "^7.0.0",
"css-loader": "^3.6.0", "css-loader": "^3.6.0",
"eslint": "^8.12.0", "eslint": "^8.32.0",
"jest": "^27.5.1", "jest": "^27.5.1",
"jest-cli": "^27.5.1", "jest-cli": "^27.5.1",
"postcss-loader": "^3.0.0", "postcss": "^8.4.7",
"postcss-loader": "^4.3.0",
"react": "^17.0.2", "react": "^17.0.2",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sass-loader": "^10.2.1", "sass-loader": "^10.2.1",
@ -168,6 +171,8 @@
}, },
"pnpm": { "pnpm": {
"overrides": { "overrides": {
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.2",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2" "react-dom": "^17.0.2"
} }

View File

@ -3,7 +3,7 @@
exports[`AbbreviatedCard it renders correctly 1`] = ` exports[`AbbreviatedCard it renders correctly 1`] = `
<div> <div>
<div <div
class="components-surface components-card woocommerce-abbreviated-card css-1vyvcpq-View-Surface-getBorders-primary-Card-rounded em57xhy0" class="components-surface components-card woocommerce-abbreviated-card css-nsno0f-View-Surface-getBorders-primary-Card-rounded em57xhy0"
data-wp-c16t="true" data-wp-c16t="true"
data-wp-component="Card" data-wp-component="Card"
> >
@ -11,7 +11,7 @@ exports[`AbbreviatedCard it renders correctly 1`] = `
class="css-mgwsf4-View-Content em57xhy0" class="css-mgwsf4-View-Content em57xhy0"
> >
<div <div
class="components-card__body components-card-body css-1sfrl79-View-Body-borderRadius em57xhy0" class="components-card__body components-card-body css-1i4jx7i-View-Body-borderRadius em57xhy0"
data-wp-c16t="true" data-wp-c16t="true"
data-wp-component="CardBody" data-wp-component="CardBody"
> >

View File

@ -7,8 +7,7 @@ import { get, find, isArray } from 'lodash';
import interpolateComponents from '@automattic/interpolate-components'; import interpolateComponents from '@automattic/interpolate-components';
import classnames from 'classnames'; import classnames from 'classnames';
import { sprintf, __, _x } from '@wordpress/i18n'; import { sprintf, __, _x } from '@wordpress/i18n';
import { CurrencyFactory } from '@woocommerce/currency';
import CurrencyFactory from '@woocommerce/currency';
/** /**
* Internal dependencies * Internal dependencies

View File

@ -192,7 +192,7 @@ export const compareStrings = (
) => { ) => {
const string1 = s1.split( splitChar ); const string1 = s1.split( splitChar );
const string2 = s2.split( splitChar ); const string2 = s2.split( splitChar );
const diff = new Array(); const diff = [];
const long = s1.length > s2.length ? string1 : string2; const long = s1.length > s2.length ? string1 : string2;
for ( let x = 0; x < long.length; x++ ) { for ( let x = 0; x < long.length; x++ ) {
// eslint-disable-next-line no-unused-expressions // eslint-disable-next-line no-unused-expressions

View File

@ -131,7 +131,8 @@ export const DateTimePickerControl: React.FC< DateTimePickerControlProps > = ( {
const formatDateTimeForDisplay = useCallback( const formatDateTimeForDisplay = useCallback(
( dateTime: Moment ) => { ( dateTime: Moment ) => {
return dateTime.isValid() return dateTime.isValid()
? formatDate( displayFormat, dateTime.local() ) ? // @ts-expect-error TODO - fix this type error with moment
formatDate( displayFormat, dateTime.local() )
: dateTime.creationData().input?.toString() || ''; : dateTime.creationData().input?.toString() || '';
}, },
[ displayFormat ] [ displayFormat ]

View File

@ -8,7 +8,7 @@ import { createElement, useCallback, useState } from '@wordpress/element';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { DateTimePickerControl, defaultDateFormat } from '../'; import { DateTimePickerControl } from '../';
export default { export default {
title: 'WooCommerce Admin/components/DateTimePickerControl', title: 'WooCommerce Admin/components/DateTimePickerControl',

View File

@ -103,6 +103,7 @@ describe( 'DateTimePickerControl', () => {
const input = container.querySelector( 'input' ); const input = container.querySelector( 'input' );
expect( input?.value ).toBe( expect( input?.value ).toBe(
// @ts-expect-error TODO - fix this type error with moment
formatDate( default24HourDateTimeFormat, dateTime ) formatDate( default24HourDateTimeFormat, dateTime )
); );
} ); } );
@ -122,6 +123,7 @@ describe( 'DateTimePickerControl', () => {
expect( input?.value ).toBe( expect( input?.value ).toBe(
formatDate( formatDate(
default24HourDateTimeFormat, default24HourDateTimeFormat,
// @ts-expect-error TODO - fix this type error with moment
moment.utc( ambiguousISODateTimeString ).local() moment.utc( ambiguousISODateTimeString ).local()
) )
); );
@ -142,6 +144,7 @@ describe( 'DateTimePickerControl', () => {
expect( input?.value ).toBe( expect( input?.value ).toBe(
formatDate( formatDate(
default24HourDateTimeFormat, default24HourDateTimeFormat,
// @ts-expect-error TODO - fix this type error with moment
moment.utc( unambiguousISODateTimeString ).local() moment.utc( unambiguousISODateTimeString ).local()
) )
); );
@ -159,6 +162,7 @@ describe( 'DateTimePickerControl', () => {
const input = container.querySelector( 'input' ); const input = container.querySelector( 'input' );
expect( input?.value ).toBe( expect( input?.value ).toBe(
// @ts-expect-error TODO - fix this type error with moment
formatDate( default12HourDateTimeFormat, dateTime ) formatDate( default12HourDateTimeFormat, dateTime )
); );
} ); } );
@ -175,6 +179,7 @@ describe( 'DateTimePickerControl', () => {
); );
const input = container.querySelector( 'input' ); const input = container.querySelector( 'input' );
// @ts-expect-error TODO - fix this type error with moment
expect( input?.value ).toBe( formatDate( dateTimeFormat, dateTime ) ); expect( input?.value ).toBe( formatDate( dateTimeFormat, dateTime ) );
} ); } );
@ -198,6 +203,7 @@ describe( 'DateTimePickerControl', () => {
const input = container.querySelector( 'input' ); const input = container.querySelector( 'input' );
expect( input?.value ).toBe( expect( input?.value ).toBe(
// @ts-expect-error TODO - fix this type error with moment
formatDate( default24HourDateTimeFormat, updatedDateTime ) formatDate( default24HourDateTimeFormat, updatedDateTime )
); );
} ); } );

View File

@ -0,0 +1,39 @@
/**
* External dependencies
*/
import { useEffect, useState } from 'react';
/**
* Internal dependencies
*/
import { TreeItemProps } from '../types';
export function useExpander( {
shouldItemBeExpanded,
item,
}: Pick< TreeItemProps, 'shouldItemBeExpanded' | 'item' > ) {
const [ isExpanded, setExpanded ] = useState( false );
useEffect( () => {
if (
item.children?.length &&
typeof shouldItemBeExpanded === 'function'
) {
setExpanded( shouldItemBeExpanded( item ) );
}
}, [ item, shouldItemBeExpanded ] );
function onExpand() {
setExpanded( true );
}
function onCollapse() {
setExpanded( false );
}
function onToggleExpand() {
setExpanded( ( prev ) => ! prev );
}
return { isExpanded, onExpand, onCollapse, onToggleExpand };
}

View File

@ -0,0 +1,31 @@
/**
* External dependencies
*/
import { useMemo } from 'react';
/**
* Internal dependencies
*/
import { CheckedStatus, TreeItemProps } from '../types';
export function useHighlighter( {
item,
multiple,
checkedStatus,
shouldItemBeHighlighted,
}: Pick< TreeItemProps, 'item' | 'multiple' | 'shouldItemBeHighlighted' > & {
checkedStatus: CheckedStatus;
} ) {
const isHighlighted = useMemo( () => {
if ( typeof shouldItemBeHighlighted === 'function' ) {
if ( multiple || item.children.length === 0 ) {
return shouldItemBeHighlighted( item );
}
}
if ( ! multiple ) {
return checkedStatus === 'checked';
}
}, [ item, multiple, checkedStatus, shouldItemBeHighlighted ] );
return { isHighlighted };
}

View File

@ -0,0 +1,173 @@
/**
* External dependencies
*/
/**
* Internal dependencies
*/
import { LinkedTree } from '../types';
function getFirstChild(
currentHeading: HTMLDivElement
): HTMLLabelElement | null {
const parentTreeItem = currentHeading?.closest< HTMLDivElement >(
'.experimental-woocommerce-tree-item'
);
const firstSubTreeItem = parentTreeItem?.querySelector(
'.experimental-woocommerce-tree > .experimental-woocommerce-tree-item'
);
const label = firstSubTreeItem?.querySelector< HTMLLabelElement >(
'.experimental-woocommerce-tree-item__heading > .experimental-woocommerce-tree-item__label'
);
return label ?? null;
}
function getFirstAncestor(
currentHeading: HTMLDivElement
): HTMLLabelElement | null {
const parentTree = currentHeading?.closest< HTMLDivElement >(
'.experimental-woocommerce-tree'
);
const grandParentTreeItem = parentTree?.closest< HTMLDivElement >(
'.experimental-woocommerce-tree-item'
);
const label = grandParentTreeItem?.querySelector< HTMLLabelElement >(
'.experimental-woocommerce-tree-item__heading > .experimental-woocommerce-tree-item__label'
);
return label ?? null;
}
function getAllHeadings(
currentHeading: HTMLDivElement
): NodeListOf< HTMLDivElement > | undefined {
const rootTree = currentHeading.closest< HTMLDivElement >(
'.experimental-woocommerce-tree--level-1'
);
return rootTree?.querySelectorAll< HTMLDivElement >(
'.experimental-woocommerce-tree-item > .experimental-woocommerce-tree-item__heading'
);
}
const step = {
ArrowDown: 1,
ArrowUp: -1,
};
function getNextFocusableElement(
currentHeading: HTMLDivElement,
code: 'ArrowDown' | 'ArrowUp'
): HTMLLabelElement | null {
const headingsNodeList = getAllHeadings( currentHeading );
if ( ! headingsNodeList ) return null;
let currentHeadingIndex = 0;
for ( const heading of headingsNodeList.values() ) {
if ( heading === currentHeading ) break;
currentHeadingIndex++;
}
if (
currentHeadingIndex < 0 ||
currentHeadingIndex >= headingsNodeList.length
) {
return null;
}
const heading = headingsNodeList.item(
currentHeadingIndex + ( step[ code ] ?? 0 )
);
return heading?.querySelector< HTMLLabelElement >(
'.experimental-woocommerce-tree-item__label'
);
}
function getFirstFocusableElement(
currentHeading: HTMLDivElement
): HTMLLabelElement | null {
const headingsNodeList = getAllHeadings( currentHeading );
if ( ! headingsNodeList ) return null;
return headingsNodeList
.item( 0 )
.querySelector< HTMLLabelElement >(
'.experimental-woocommerce-tree-item__label'
);
}
function getLastFocusableElement(
currentHeading: HTMLDivElement
): HTMLLabelElement | null {
const headingsNodeList = getAllHeadings( currentHeading );
if ( ! headingsNodeList ) return null;
return headingsNodeList
.item( headingsNodeList.length - 1 )
.querySelector< HTMLLabelElement >(
'.experimental-woocommerce-tree-item__label'
);
}
export function useKeyboard( {
item,
isExpanded,
onExpand,
onCollapse,
onToggleExpand,
}: {
item: LinkedTree;
isExpanded: boolean;
onExpand(): void;
onCollapse(): void;
onToggleExpand(): void;
} ) {
function onKeyDown( event: React.KeyboardEvent< HTMLDivElement > ) {
if ( event.code === 'ArrowRight' ) {
event.preventDefault();
if ( item.children.length > 0 ) {
if ( isExpanded ) {
const element = getFirstChild( event.currentTarget );
return element?.focus();
}
onExpand();
}
}
if ( event.code === 'ArrowLeft' ) {
event.preventDefault();
if ( ! isExpanded && item.parent ) {
const element = getFirstAncestor( event.currentTarget );
return element?.focus();
}
if ( item.children.length > 0 ) {
onCollapse();
}
}
if ( event.code === 'Enter' ) {
event.preventDefault();
if ( item.children.length > 0 ) {
onToggleExpand();
}
}
if ( event.code === 'ArrowDown' || event.code === 'ArrowUp' ) {
event.preventDefault();
const element = getNextFocusableElement(
event.currentTarget,
event.code
);
element?.focus();
}
if ( event.code === 'Home' ) {
event.preventDefault();
const element = getFirstFocusableElement( event.currentTarget );
element?.focus();
}
if ( event.code === 'End' ) {
event.preventDefault();
const element = getLastFocusableElement( event.currentTarget );
element?.focus();
}
}
return { onKeyDown };
}

View File

@ -0,0 +1,167 @@
/**
* External dependencies
*/
import { useMemo } from 'react';
/**
* Internal dependencies
*/
import { CheckedStatus, Item, LinkedTree, TreeItemProps } from '../types';
let selectedItemsMap: Record< string, number > = {};
let indeterminateMemo: Record< string, boolean > = {};
function getDeepChildren( item: LinkedTree ) {
if ( item.children.length ) {
const children = item.children.map( ( { data } ) => data );
item.children.forEach( ( child ) => {
children.push( ...getDeepChildren( child ) );
} );
return children;
}
return [];
}
function isIndeterminate(
selectedItems: Record< string, number >,
children?: LinkedTree[],
memo: Record< string, boolean > = indeterminateMemo
): boolean {
if ( children?.length ) {
for ( const child of children ) {
if ( child.data.value in indeterminateMemo ) {
return true;
}
const isChildSelected = child.data.value in selectedItems;
if (
! isChildSelected ||
isIndeterminate( selectedItems, child.children, memo )
) {
indeterminateMemo[ child.data.value ] = true;
return true;
}
}
}
return false;
}
function mapSelectedItems(
selected: Item | Item[] = []
): Record< string, number > {
const selectedArray = Array.isArray( selected ) ? selected : [ selected ];
return selectedArray.reduce(
( map, selectedItem, index ) => ( {
...map,
[ selectedItem.value ]: index,
} ),
{} as Record< string, number >
);
}
function hasSelectedSibblingChildren(
children: LinkedTree[],
values: Item[],
selectedItems: Record< string, number >
) {
return children.some( ( child ) => {
const isChildSelected = child.data.value in selectedItems;
if ( ! isChildSelected ) return false;
return ! values.some(
( childValue ) => childValue.value === child.data.value
);
} );
}
export function useSelection( {
item,
multiple,
selected,
level,
index,
onSelect,
onRemove,
}: Pick<
TreeItemProps,
| 'item'
| 'multiple'
| 'selected'
| 'level'
| 'index'
| 'onSelect'
| 'onRemove'
> ) {
const selectedItems = useMemo( () => {
if ( level === 1 && index === 0 ) {
selectedItemsMap = mapSelectedItems( selected );
indeterminateMemo = {} as Record< string, boolean >;
}
return selectedItemsMap;
}, [ selected, level, index ] );
const checkedStatus: CheckedStatus = useMemo( () => {
if ( item.data.value in selectedItems ) {
if ( multiple && isIndeterminate( selectedItems, item.children ) ) {
return 'indeterminate';
}
return 'checked';
}
return 'unchecked';
}, [ selectedItems, item, multiple ] );
function onSelectChild( checked: boolean ) {
let value: Item | Item[] = item.data;
if ( multiple ) {
value = [ item.data ];
if ( item.children.length ) {
value.push( ...getDeepChildren( item ) );
}
} else if ( item.children?.length ) {
return;
}
if ( checked ) {
if ( typeof onSelect === 'function' ) {
onSelect( value );
}
} else if ( typeof onRemove === 'function' ) {
onRemove( value );
}
}
function onSelectChildren( value: Item | Item[] ) {
if ( typeof onSelect !== 'function' ) return;
if ( multiple ) {
value = [ item.data, ...( value as Item[] ) ];
}
onSelect( value );
}
function onRemoveChildren( value: Item | Item[] ) {
if ( typeof onRemove !== 'function' ) return;
if ( multiple && item.children?.length ) {
const hasSelectedSibbling = hasSelectedSibblingChildren(
item.children,
value as Item[],
selectedItems
);
if ( ! hasSelectedSibbling ) {
value = [ item.data, ...( value as Item[] ) ];
}
}
onRemove( value );
}
return {
multiple,
selected,
checkedStatus,
onSelectChild,
onSelectChildren,
onRemoveChildren,
};
}

View File

@ -2,29 +2,102 @@
* External dependencies * External dependencies
*/ */
import React from 'react'; import React from 'react';
import { useInstanceId } from '@wordpress/compose';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { TreeItemProps } from '../types'; import { TreeItemProps } from '../types';
import { useExpander } from './use-expander';
import { useHighlighter } from './use-highlighter';
import { useKeyboard } from './use-keyboard';
import { useSelection } from './use-selection';
export function useTreeItem( { item, level, ...props }: TreeItemProps ) { export function useTreeItem( {
item,
level,
multiple,
selected,
index,
getLabel,
shouldItemBeExpanded,
shouldItemBeHighlighted,
onSelect,
onRemove,
...props
}: TreeItemProps ) {
const nextLevel = level + 1; const nextLevel = level + 1;
const expander = useExpander( {
item,
shouldItemBeExpanded,
} );
const selection = useSelection( {
item,
multiple,
selected,
level,
index,
onSelect,
onRemove,
} );
const highlighter = useHighlighter( {
item,
checkedStatus: selection.checkedStatus,
multiple,
shouldItemBeHighlighted,
} );
const subTreeId = `experimental-woocommerce-tree__group-${ useInstanceId(
useTreeItem
) }`;
const { onKeyDown } = useKeyboard( {
...expander,
item,
} );
return { return {
item, item,
level: nextLevel, level: nextLevel,
expander,
selection,
highlighter,
getLabel,
treeItemProps: { treeItemProps: {
...props, ...props,
role: 'none',
}, },
headingProps: { headingProps: {
role: 'treeitem',
'aria-selected': selection.checkedStatus !== 'unchecked',
'aria-expanded': item.children.length
? expander.isExpanded
: undefined,
'aria-owns':
item.children.length && expander.isExpanded
? subTreeId
: undefined,
style: { style: {
'--level': level, '--level': level,
} as React.CSSProperties, } as React.CSSProperties,
onKeyDown,
}, },
treeProps: { treeProps: {
id: subTreeId,
items: item.children, items: item.children,
level: nextLevel, level: nextLevel,
multiple: selection.multiple,
selected: selection.selected,
role: 'group',
'aria-label': item.data.label,
getItemLabel: getLabel,
shouldItemBeExpanded,
shouldItemBeHighlighted,
onSelect: selection.onSelectChildren,
onRemove: selection.onRemoveChildren,
}, },
}; };
} }

View File

@ -7,15 +7,36 @@
*/ */
import { TreeProps } from '../types'; import { TreeProps } from '../types';
export function useTree( { ref, items, level = 1, ...props }: TreeProps ) { export function useTree( {
ref,
items,
level = 1,
role = 'tree',
multiple,
selected,
getItemLabel,
shouldItemBeExpanded,
shouldItemBeHighlighted,
onSelect,
onRemove,
...props
}: TreeProps ) {
return { return {
level, level,
items, items,
treeProps: { treeProps: {
...props, ...props,
role,
}, },
treeItemProps: { treeItemProps: {
level, level,
multiple,
selected,
getLabel: getItemLabel,
shouldItemBeExpanded,
shouldItemBeHighlighted,
onSelect,
onRemove,
}, },
}; };
} }

View File

@ -1,14 +1,16 @@
/** /**
* External dependencies * External dependencies
*/ */
import { BaseControl } from '@wordpress/components'; import interpolate from '@automattic/interpolate-components';
import React, { createElement } from 'react'; import { BaseControl, TextControl } from '@wordpress/components';
import React, { createElement, useCallback, useRef, useState } from 'react';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { TreeControl } from '../tree-control'; import { TreeControl } from '../tree-control';
import { Item } from '../types'; import { Item, LinkedTree } from '../types';
import '../tree.scss';
const listItems: Item[] = [ const listItems: Item[] = [
{ value: '1', label: 'Technology' }, { value: '1', label: 'Technology' },
@ -36,6 +38,222 @@ export const SimpleTree: React.FC = () => {
); );
}; };
function shouldItemBeExpanded( item: LinkedTree, filter: string ) {
if ( ! filter || ! item.children?.length ) return false;
return item.children.some( ( child ) => {
if ( new RegExp( filter, 'ig' ).test( child.data.label ) ) {
return true;
}
return shouldItemBeExpanded( child, filter );
} );
}
export const ExpandOnFilter: React.FC = () => {
const [ filter, setFilter ] = useState( '' );
return (
<>
<TextControl value={ filter } onChange={ setFilter } />
<BaseControl label="Expand on filter" id="expand-on-filter">
<TreeControl
id="expand-on-filter"
items={ listItems }
shouldItemBeExpanded={ ( item ) =>
shouldItemBeExpanded( item, filter )
}
/>
</BaseControl>
</>
);
};
export const CustomItemLabel: React.FC = () => {
function renderCustomItemLabel( item: LinkedTree ) {
return (
<div style={ { display: 'flex', gap: 8 } }>
<div
style={ {
width: 36,
height: 36,
backgroundColor: '#ccc',
borderRadius: 2,
} }
/>
<div
style={ {
display: 'flex',
flexDirection: 'column',
} }
>
<strong>{ item.data.label }</strong>
<small>Some item description</small>
</div>
</div>
);
}
return (
<BaseControl label="Custom item label" id="custom-item-label">
<TreeControl
id="custom-item-label"
items={ listItems }
getItemLabel={ renderCustomItemLabel }
/>
</BaseControl>
);
};
function getItemLabel( item: LinkedTree, text: string ) {
return (
<span>
{ text
? interpolate( {
mixedString: item.data.label.replace(
new RegExp( text, 'ig' ),
( group ) => `{{bold}}${ group }{{/bold}}`
),
components: {
bold: <b />,
},
} )
: item.data.label }
</span>
);
}
export const CustomItemLabelOnSearch: React.FC = () => {
const [ text, setText ] = useState( '' );
return (
<>
<TextControl value={ text } onChange={ setText } />
<BaseControl
label="Custom item label on search"
id="custom-item-label-on-search"
>
<TreeControl
id="custom-item-label-on-search"
items={ listItems }
getItemLabel={ ( item ) => getItemLabel( item, text ) }
shouldItemBeExpanded={ useCallback(
( item ) => shouldItemBeExpanded( item, text ),
[ text ]
) }
/>
</BaseControl>
</>
);
};
export const SelectionSingle: React.FC = () => {
const [ selected, setSelected ] = useState( listItems[ 1 ] );
return (
<>
<BaseControl label="Single selection" id="single-selection">
<TreeControl
id="single-selection"
items={ listItems }
selected={ selected }
onSelect={ ( value: Item ) => setSelected( value ) }
/>
</BaseControl>
<pre>{ JSON.stringify( selected, null, 2 ) }</pre>
</>
);
};
export const SelectionMultiple: React.FC = () => {
const [ selected, setSelected ] = useState( [
listItems[ 0 ],
listItems[ 1 ],
] );
function handleSelect( values: Item[] ) {
setSelected( ( items ) => {
const newItems = values.filter(
( { value } ) =>
! items.some( ( item ) => item.value === value )
);
return [ ...items, ...newItems ];
} );
}
function handleRemove( values: Item[] ) {
setSelected( ( items ) =>
items.filter(
( item ) =>
! values.some( ( { value } ) => item.value === value )
)
);
}
return (
<>
<BaseControl label="Multiple selection" id="multiple-selection">
<TreeControl
id="multiple-selection"
items={ listItems }
multiple
selected={ selected }
onSelect={ handleSelect }
onRemove={ handleRemove }
/>
</BaseControl>
<pre>{ JSON.stringify( selected, null, 2 ) }</pre>
</>
);
};
function getFirstMatchingItem(
item: LinkedTree,
text: string,
memo: Record< string, string >
) {
if ( ! text ) return false;
if ( memo[ text ] === item.data.value ) return true;
const matcher = new RegExp( text, 'ig' );
if ( matcher.test( item.data.label ) ) {
if ( ! memo[ text ] ) {
memo[ text ] = item.data.value;
return true;
}
}
return false;
}
export const HighlightFirstMatchingItem: React.FC = () => {
const [ text, setText ] = useState( '' );
const memo = useRef< Record< string, string > >( {} );
return (
<>
<TextControl value={ text } onChange={ setText } />
<BaseControl
label="Highlight first matching item"
id="highlight-first-matching-item"
>
<TreeControl
id="highlight-first-matching-item"
items={ listItems }
getItemLabel={ ( item ) => getItemLabel( item, text ) }
shouldItemBeExpanded={ useCallback(
( item ) => shouldItemBeExpanded( item, text ),
[ text ]
) }
shouldItemBeHighlighted={ ( item ) =>
getFirstMatchingItem( item, text, memo.current )
}
/>
</BaseControl>
</>
);
};
export default { export default {
title: 'WooCommerce Admin/experimental/TreeControl', title: 'WooCommerce Admin/experimental/TreeControl',
component: TreeControl, component: TreeControl,

View File

@ -1,12 +1,21 @@
$control-size: $gap-large;
.experimental-woocommerce-tree-item { .experimental-woocommerce-tree-item {
margin: 0; margin: 0;
&--highlighted {
> .experimental-woocommerce-tree-item__heading {
background-color: $gray-100;
}
}
&__heading { &__heading {
display: flex; display: flex;
flex-grow: 1; flex-grow: 1;
gap: $gap-smaller; gap: $gap-smaller;
min-height: $gap-largest; min-height: $gap-largest;
padding: 0 $gap-small 0 calc( ( var( --level ) - 1 ) * ( $gap + $gap-small ) + $gap-small ); padding: 0 $gap-small 0
calc( ( var( --level ) - 1 ) * ( $gap + $gap-small ) + $gap-small );
border-radius: 2px; border-radius: 2px;
&:hover, &:hover,
@ -17,9 +26,10 @@
&:hover, &:hover,
&:focus-within { &:focus-within {
background-color: $gray-0; background-color: $gray-100;
} }
} }
&__label { &__label {
display: flex; display: flex;
flex-grow: 1; flex-grow: 1;
@ -30,5 +40,50 @@
> span { > span {
display: block; display: block;
} }
.components-base-control__field {
margin: 0;
}
.components-radio-control__input {
@include screen-reader-only();
}
.components-checkbox-control__label {
display: none;
}
.components-checkbox-control__input-container {
display: block;
width: $control-size;
height: $control-size;
}
svg.components-checkbox-control__checked,
svg.components-checkbox-control__indeterminate,
.components-checkbox-control__input[type='checkbox'] {
position: absolute;
border-color: $gray-700;
width: $control-size;
height: $control-size;
top: 0;
left: 0;
&:focus {
outline: none;
box-shadow: none;
}
}
}
&__expander {
display: flex;
align-items: center;
.components-button {
padding: 0;
height: $control-size;
width: $control-size;
min-width: $control-size;
}
} }
} }

View File

@ -1,7 +1,9 @@
/** /**
* External dependencies * External dependencies
*/ */
import { Button, CheckboxControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { chevronDown, chevronUp } from '@wordpress/icons';
import classNames from 'classnames'; import classNames from 'classnames';
import { createElement, forwardRef } from 'react'; import { createElement, forwardRef } from 'react';
@ -16,7 +18,16 @@ export const TreeItem = forwardRef( function ForwardedTreeItem(
props: TreeItemProps, props: TreeItemProps,
ref: React.ForwardedRef< HTMLLIElement > ref: React.ForwardedRef< HTMLLIElement >
) { ) {
const { item, treeItemProps, headingProps, treeProps } = useTreeItem( { const {
item,
treeItemProps,
headingProps,
treeProps,
expander: { isExpanded, onToggleExpand },
selection,
highlighter: { isHighlighted },
getLabel,
} = useTreeItem( {
...props, ...props,
ref, ref,
} ); } );
@ -26,19 +37,66 @@ export const TreeItem = forwardRef( function ForwardedTreeItem(
{ ...treeItemProps } { ...treeItemProps }
className={ classNames( className={ classNames(
treeItemProps.className, treeItemProps.className,
'experimental-woocommerce-tree-item' 'experimental-woocommerce-tree-item',
{
'experimental-woocommerce-tree-item--highlighted':
isHighlighted,
}
) } ) }
> >
<div <div
{ ...headingProps } { ...headingProps }
className="experimental-woocommerce-tree-item__heading" className="experimental-woocommerce-tree-item__heading"
> >
<div className="experimental-woocommerce-tree-item__label"> { /* eslint-disable-next-line jsx-a11y/label-has-for */ }
<span>{ item.data.label }</span> <label className="experimental-woocommerce-tree-item__label">
</div> { selection.multiple ? (
<CheckboxControl
indeterminate={
selection.checkedStatus === 'indeterminate'
}
checked={ selection.checkedStatus === 'checked' }
onChange={ selection.onSelectChild }
/>
) : (
<input
type="radio"
checked={ selection.checkedStatus === 'checked' }
className="components-radio-control__input"
onChange={ ( event ) =>
selection.onSelectChild(
event.currentTarget.checked
)
}
/>
) }
{ typeof getLabel === 'function' ? (
getLabel( item )
) : (
<span>{ item.data.label }</span>
) }
</label>
{ Boolean( item.children?.length ) && (
<div className="experimental-woocommerce-tree-item__expander">
<Button
icon={ isExpanded ? chevronUp : chevronDown }
onClick={ onToggleExpand }
className="experimental-woocommerce-tree-item__expander"
aria-label={
isExpanded
? __( 'Collapse', 'woocommerce' )
: __( 'Expand', 'woocommerce' )
}
/>
</div>
) }
</div> </div>
{ Boolean( item.children.length ) && <Tree { ...treeProps } /> } { Boolean( item.children.length ) && isExpanded && (
<Tree { ...treeProps } />
) }
</li> </li>
); );
} ); } );

View File

@ -30,11 +30,12 @@ export const Tree = forwardRef( function ForwardedTree(
`experimental-woocommerce-tree--level-${ level }` `experimental-woocommerce-tree--level-${ level }`
) } ) }
> >
{ items.map( ( child ) => ( { items.map( ( child, index ) => (
<TreeItem <TreeItem
{ ...treeItemProps } { ...treeItemProps }
key={ child.data.value } key={ child.data.value }
item={ child } item={ child }
index={ index }
/> />
) ) } ) ) }
</ol> </ol>

View File

@ -10,21 +10,106 @@ export interface LinkedTree {
children: LinkedTree[]; children: LinkedTree[];
} }
export type TreeProps = React.DetailedHTMLProps< export type CheckedStatus = 'checked' | 'unchecked' | 'indeterminate';
React.OlHTMLAttributes< HTMLOListElement >,
HTMLOListElement type BaseTreeProps = {
> & { /**
level?: number; * It contians one item if `multiple` value is false or
items: LinkedTree[]; * a list of items if it is true.
*/
selected?: Item | Item[];
/**
* Whether the tree items are single or multiple selected.
*/
multiple?: boolean;
/**
* When `multiple` is true and a child item is selected, all its
* ancestors and its descendants are also selected. If it's false
* only the clicked item is selected.
*
* @param value The selection
*/
onSelect?( value: Item | Item[] ): void;
/**
* When `multiple` is true and a child item is unselected, all its
* ancestors (if no sibblings are selected) and its descendants
* are also unselected. If it's false only the clicked item is
* unselected.
*
* @param value The unselection
*/
onRemove?( value: Item | Item[] ): void;
/**
* It provides a way to determine whether the current rendering
* item is highlighted or not from outside the tree.
*
* @example
* <Tree
* shouldItemBeHighlighted={ isFirstChild }
* />
*
* @param item The current linked tree item, useful to
* traverse the entire linked tree from this item.
*
* @see {@link LinkedTree}
*/
shouldItemBeHighlighted?( item: LinkedTree ): boolean;
}; };
export type TreeItemProps = React.DetailedHTMLProps< export type TreeProps = BaseTreeProps &
React.LiHTMLAttributes< HTMLLIElement >, Omit<
HTMLLIElement React.DetailedHTMLProps<
> & { React.OlHTMLAttributes< HTMLOListElement >,
level: number; HTMLOListElement
item: LinkedTree; >,
}; 'onSelect'
> & {
level?: number;
items: LinkedTree[];
/** It gives a way to render a different Element as the
* tree item label.
*
* @example
* <Tree
* getItemLabel={ ( item ) => <span>${ item.data.label }</span> }
* />
*
* @param item The current rendering tree item
*
* @see {@link LinkedTree}
*/
getItemLabel?( item: LinkedTree ): JSX.Element;
/**
* Return if the tree item passed in should be expanded.
*
* @example
* <Tree
* shouldItemBeExpanded={
* ( item ) => checkExpanded( item, filter )
* }
* />
*
* @param item The tree item to determine if should be expanded.
*
* @see {@link LinkedTree}
*/
shouldItemBeExpanded?( item: LinkedTree ): boolean;
};
export type TreeItemProps = BaseTreeProps &
Omit<
React.DetailedHTMLProps<
React.LiHTMLAttributes< HTMLLIElement >,
HTMLLIElement
>,
'onSelect'
> & {
level: number;
item: LinkedTree;
index: number;
getLabel?( item: LinkedTree ): JSX.Element;
shouldItemBeExpanded?( item: LinkedTree ): boolean;
};
export type TreeControlProps = Omit< TreeProps, 'items' | 'level' > & { export type TreeControlProps = Omit< TreeProps, 'items' | 'level' > & {
items: Item[]; items: Item[];

View File

@ -0,0 +1,9 @@
import * as components from '@wordpress/components';
declare module '@wordpress/components' {
declare namespace CheckboxControl {
interface Props {
indeterminate?: boolean;
}
}
}

View File

@ -7,7 +7,7 @@ import { find } from 'lodash';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { updateQueryString } from '@woocommerce/navigation'; import { updateQueryString } from '@woocommerce/navigation';
import { getDateParamsFromQuery, getCurrentDates } from '@woocommerce/date'; import { getDateParamsFromQuery, getCurrentDates } from '@woocommerce/date';
import CurrencyFactory from '@woocommerce/currency'; import { CurrencyFactory } from '@woocommerce/currency';
/** /**
* Internal dependencies * Internal dependencies

View File

@ -88,10 +88,6 @@ export { createOrderedChildren, sortFillsByOrder } from './utils';
export { WooProductFieldItem as __experimentalWooProductFieldItem } from './woo-product-field-item'; export { WooProductFieldItem as __experimentalWooProductFieldItem } from './woo-product-field-item';
export { WooProductSectionItem as __experimentalWooProductSectionItem } from './woo-product-section-item'; export { WooProductSectionItem as __experimentalWooProductSectionItem } from './woo-product-section-item';
export { WooProductTabItem as __experimentalWooProductTabItem } from './woo-product-tab-item'; export { WooProductTabItem as __experimentalWooProductTabItem } from './woo-product-tab-item';
export {
ProductSectionLayout as __experimentalProductSectionLayout,
ProductFieldSection as __experimentalProductFieldSection,
} from './product-section-layout';
export * from './product-fields'; export * from './product-fields';
export { export {
SlotContextProvider, SlotContextProvider,
@ -99,3 +95,11 @@ export {
SlotContextType, SlotContextType,
SlotContextHelpersType, SlotContextHelpersType,
} from './slot-context'; } from './slot-context';
export { TreeControl as __experimentalTreeControl } from './experimental-tree-control';
export { default as TreeSelectControl } from './tree-select-control';
// Exports below can be removed once the @woocommerce/product-editor package is released.
export {
ProductSectionLayout as __experimentalProductSectionLayout,
ProductFieldSection as __experimentalProductFieldSection,
} from './product-section-layout';

View File

@ -12,11 +12,6 @@ import { useState } from '@wordpress/element';
import { MediaUploader } from '../'; import { MediaUploader } from '../';
import { File } from '../types'; import { File } from '../types';
declare let Blob: {
prototype: Blob;
new (): Blob;
};
const MockMediaUpload = ( { onSelect, render } ) => { const MockMediaUpload = ( { onSelect, render } ) => {
const [ isOpen, setOpen ] = useState( false ); const [ isOpen, setOpen ] = useState( false );

View File

@ -9,7 +9,7 @@ import {
useState, useState,
useEffect, useEffect,
} from '@wordpress/element'; } from '@wordpress/element';
import { SyntheticEvent } 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, InstallPluginsResponse } from '@woocommerce/data';
@ -25,6 +25,11 @@ type PluginsProps = {
pluginSlugs?: string[]; pluginSlugs?: string[];
onAbort?: () => void; onAbort?: () => void;
abortText?: string; abortText?: string;
installText?: string;
installButtonVariant?: Button.BaseProps[ 'variant' ];
learnMoreLink?: string;
learnMoreText?: string;
onLearnMore?: () => void;
}; };
export const Plugins = ( { export const Plugins = ( {
@ -34,10 +39,17 @@ export const Plugins = ( {
onError = () => null, onError = () => null,
pluginSlugs = [ 'jetpack', 'woocommerce-services' ], pluginSlugs = [ 'jetpack', 'woocommerce-services' ],
onSkip, onSkip,
installText = __( 'Install & enable', 'woocommerce' ),
skipText = __( 'No thanks', 'woocommerce' ), skipText = __( 'No thanks', 'woocommerce' ),
abortText = __( 'Abort', 'woocommerce' ), abortText = __( 'Abort', 'woocommerce' ),
installButtonVariant = 'primary',
learnMoreLink,
learnMoreText = __( 'Learn more', 'woocommerce' ),
onLearnMore,
}: PluginsProps ) => { }: PluginsProps ) => {
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
const [ hasBeenClicked, setHasBeenClicked ] = useState( false );
const { installAndActivatePlugins } = useDispatch( PLUGINS_STORE_NAME ); const { installAndActivatePlugins } = useDispatch( PLUGINS_STORE_NAME );
const { isRequesting } = useSelect( ( select ) => { const { isRequesting } = useSelect( ( select ) => {
const { getActivePlugins, getInstalledPlugins, isPluginsRequesting } = const { getActivePlugins, getInstalledPlugins, isPluginsRequesting } =
@ -52,48 +64,56 @@ export const Plugins = ( {
}; };
} ); } );
const handleErrors = ( const handleErrors = useCallback(
errors: unknown, ( errors: unknown, response: InstallPluginsResponse ) => {
response: InstallPluginsResponse setHasErrors( true );
) => {
setHasErrors( true );
onError( errors, response ); onError( errors, response );
}; },
[ onError ]
);
const handleSuccess = ( const handleSuccess = useCallback(
plugins: string[], ( plugins: string[], response: InstallPluginsResponse ) => {
response: InstallPluginsResponse onComplete( plugins, response );
) => { },
onComplete( plugins, response ); [ onComplete ]
}; );
const installAndActivate = async ( const installAndActivate = useCallback(
event?: SyntheticEvent< HTMLAnchorElement > async ( event?: SyntheticEvent< HTMLAnchorElement > ) => {
) => { if ( event ) {
if ( event ) { event.preventDefault();
event.preventDefault(); }
}
// Avoid double activating. // Avoid double activating.
if ( isRequesting ) { if ( isRequesting ) {
return false; return false;
} }
installAndActivatePlugins( pluginSlugs ) installAndActivatePlugins( pluginSlugs )
.then( ( response ) => { .then( ( response ) => {
handleSuccess( response.data.activated, response ); handleSuccess( response.data.activated, response );
} ) } )
.catch( ( response ) => { .catch( ( response ) => {
handleErrors( response.errors, response ); setHasBeenClicked( false );
} ); handleErrors( response.errors, response );
}; } );
},
[
handleErrors,
handleSuccess,
installAndActivatePlugins,
isRequesting,
pluginSlugs,
]
);
useEffect( () => { useEffect( () => {
if ( autoInstall ) { if ( autoInstall ) {
installAndActivate(); installAndActivate();
} }
}, [] ); }, [ autoInstall, installAndActivate ] );
if ( hasErrors ) { if ( hasErrors ) {
return ( return (
@ -131,17 +151,32 @@ export const Plugins = ( {
return ( return (
<> <>
<Button <Button
isBusy={ isRequesting } isBusy={ isRequesting && hasBeenClicked }
isPrimary variant={
onClick={ installAndActivate } isRequesting && hasBeenClicked
? 'primary' // set to primary when busy, the other variants look weird when combined with isBusy
: installButtonVariant
}
disabled={ isRequesting && hasBeenClicked }
onClick={ () => {
setHasBeenClicked( true );
installAndActivate();
} }
> >
{ __( 'Install & enable', 'woocommerce' ) } { installText }
</Button> </Button>
{ onSkip && ( { onSkip && (
<Button isTertiary onClick={ onSkip }> <Button isTertiary onClick={ onSkip }>
{ skipText } { skipText }
</Button> </Button>
) } ) }
{ learnMoreLink && (
<a href={ learnMoreLink } target="_blank" rel="noreferrer">
<Button isTertiary onClick={ onLearnMore }>
{ learnMoreText }
</Button>
</a>
) }
{ onAbort && ( { onAbort && (
<Button isTertiary onClick={ onAbort }> <Button isTertiary onClick={ onAbort }>
{ abortText } { abortText }

View File

@ -2,7 +2,7 @@
* External dependencies * External dependencies
*/ */
import { createElement } from '@wordpress/element'; import { createElement } from '@wordpress/element';
import { RadioControl, SelectControl } from '@wordpress/components'; import { SelectControl } from '@wordpress/components';
/** /**
* Internal dependencies * Internal dependencies

View File

@ -3,6 +3,7 @@
*/ */
import { createElement } from '@wordpress/element'; import { createElement } from '@wordpress/element';
import { Card, CardBody } from '@wordpress/components'; import { Card, CardBody } from '@wordpress/components';
import deprecated from '@wordpress/deprecated';
/** /**
* Internal dependencies * Internal dependencies
@ -23,17 +24,24 @@ export const ProductFieldSection: React.FC< ProductFieldSectionProps > = ( {
description, description,
className, className,
children, children,
} ) => ( } ) => {
<ProductSectionLayout deprecated( `__experimentalProductFieldSection`, {
title={ title } version: '13.0.0',
description={ description } plugin: '@woocommerce/components',
className={ className } hint: 'Moved to @woocommerce/product-editor package: import { __experimentalProductFieldSection } from @woocommerce/product-editor',
> } );
<Card> return (
<CardBody> <ProductSectionLayout
{ children } title={ title }
<WooProductFieldItem.Slot section={ id } /> description={ description }
</CardBody> className={ className }
</Card> >
</ProductSectionLayout> <Card>
); <CardBody>
{ children }
<WooProductFieldItem.Slot section={ id } />
</CardBody>
</Card>
</ProductSectionLayout>
);
};

View File

@ -2,7 +2,7 @@
* External dependencies * External dependencies
*/ */
import { Children, isValidElement, createElement } from '@wordpress/element'; import { Children, isValidElement, createElement } from '@wordpress/element';
import deprecated from '@wordpress/deprecated';
/** /**
* Internal dependencies * Internal dependencies
*/ */
@ -19,17 +19,26 @@ export const ProductSectionLayout: React.FC< ProductSectionLayoutProps > = ( {
description, description,
className, className,
children, children,
} ) => ( } ) => {
<FormSection deprecated( `__experimentalProductSectionLayout`, {
title={ title } version: '13.0.0',
description={ description } plugin: '@woocommerce/components',
className={ className } hint: 'Moved to @woocommerce/product-editor package: import { __experimentalProductSectionLayout } from @woocommerce/product-editor',
> } );
{ Children.map( children, ( child ) => { return (
if ( isValidElement( child ) && child.props.onChange ) { <FormSection
return <div className="product-field-layout">{ child }</div>; title={ title }
} description={ description }
return child; className={ className }
} ) } >
</FormSection> { Children.map( children, ( child ) => {
); if ( isValidElement( child ) && child.props.onChange ) {
return (
<div className="product-field-layout">{ child }</div>
);
}
return child;
} ) }
</FormSection>
);
};

View File

@ -82,20 +82,6 @@ export const SearchListControl = ( props ) => {
}; };
}; };
const onSelect = ( item ) => {
return () => {
if ( isSelected( item ) ) {
onRemove( item.id )();
return;
}
if ( isSingle ) {
onChange( [ item ] );
} else {
onChange( [ ...selected, item ] );
}
};
};
const isSelected = ( item ) => const isSelected = ( item ) =>
findIndex( selected, { id: item.id } ) !== -1; findIndex( selected, { id: item.id } ) !== -1;
@ -114,6 +100,20 @@ export const SearchListControl = ( props ) => {
: filteredList; : filteredList;
}; };
const onSelect = ( item ) => {
return () => {
if ( isSelected( item ) ) {
onRemove( item.id )();
return;
}
if ( isSingle ) {
onChange( [ item ] );
} else {
onChange( [ ...selected, item ] );
}
};
};
const defaultRenderItem = ( args ) => { const defaultRenderItem = ( args ) => {
return <SearchListItem { ...args } />; return <SearchListItem { ...args } />;
}; };

View File

@ -47,13 +47,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-10" for="inspector-text-control-10"
> >
Search for items Search for items
@ -246,13 +246,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-10" for="inspector-text-control-10"
> >
Search for items Search for items
@ -526,13 +526,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-0" for="inspector-text-control-0"
> >
Search for items Search for items
@ -710,13 +710,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-0" for="inspector-text-control-0"
> >
Search for items Search for items
@ -975,13 +975,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-1" for="inspector-text-control-1"
> >
Search for items Search for items
@ -1159,13 +1159,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-1" for="inspector-text-control-1"
> >
Search for items Search for items
@ -1424,13 +1424,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-9" for="inspector-text-control-9"
> >
Search for items Search for items
@ -1506,13 +1506,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-9" for="inspector-text-control-9"
> >
Search for items Search for items
@ -1669,13 +1669,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-8" for="inspector-text-control-8"
> >
Testing search label Testing search label
@ -1853,13 +1853,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-8" for="inspector-text-control-8"
> >
Testing search label Testing search label
@ -2118,13 +2118,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-4" for="inspector-text-control-4"
> >
Search for items Search for items
@ -2189,13 +2189,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-4" for="inspector-text-control-4"
> >
Search for items Search for items
@ -2341,13 +2341,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-7" for="inspector-text-control-7"
> >
Search for items Search for items
@ -2412,13 +2412,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-7" for="inspector-text-control-7"
> >
Search for items Search for items
@ -2564,13 +2564,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-5" for="inspector-text-control-5"
> >
Search for items Search for items
@ -2664,13 +2664,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-5" for="inspector-text-control-5"
> >
Search for items Search for items
@ -2845,13 +2845,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-6" for="inspector-text-control-6"
> >
Search for items Search for items
@ -2945,13 +2945,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-6" for="inspector-text-control-6"
> >
Search for items Search for items
@ -3176,13 +3176,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-2" for="inspector-text-control-2"
> >
Search for items Search for items
@ -3411,13 +3411,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-2" for="inspector-text-control-2"
> >
Search for items Search for items
@ -3768,13 +3768,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-3" for="inspector-text-control-3"
> >
Search for items Search for items
@ -4045,13 +4045,13 @@ Object {
class="woocommerce-search-list__search" class="woocommerce-search-list__search"
> >
<div <div
class="components-base-control css-wdf2ti-Wrapper e1puf3u4" class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
> >
<div <div
class="components-base-control__field css-igk9ll-StyledField e1puf3u3" class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
> >
<label <label
class="components-base-control__label css-eweeby-StyledLabel-labelStyles e1puf3u2" class="components-base-control__label css-eweeby-StyledLabel-labelStyles ej5x27r2"
for="inspector-text-control-3" for="inspector-text-control-3"
> >
Search for items Search for items

View File

@ -563,8 +563,8 @@ SelectControl.defaultProps = {
autoComplete: 'off', autoComplete: 'off',
}; };
export default compose( [ export default compose(
withSpokenMessages, withSpokenMessages,
withInstanceId, withInstanceId,
withFocusOutside, // this MUST be the innermost HOC as it calls handleFocusOutside withFocusOutside // this MUST be the innermost HOC as it calls handleFocusOutside
] )( SelectControl ); )( SelectControl );

View File

@ -169,7 +169,7 @@ describe( 'SelectControl', () => {
it( 'changes the options on search', async () => { it( 'changes the options on search', async () => {
const queriedOptions = []; const queriedOptions = [];
// eslint-disable-next-line no-shadow // eslint-disable-next-line @typescript-eslint/no-shadow
const queryOptions = ( options, searchedQuery ) => { const queryOptions = ( options, searchedQuery ) => {
if ( searchedQuery === 'test' ) { if ( searchedQuery === 'test' ) {
queriedOptions.push( { queriedOptions.push( {

View File

@ -1,7 +1,6 @@
/** /**
* External dependencies * External dependencies
*/ */
import { __ } from '@wordpress/i18n';
import { createElement, useContext } from '@wordpress/element'; import { createElement, useContext } from '@wordpress/element';
/** /**

View File

@ -1,7 +1,6 @@
/** /**
* External Dependencies * External Dependencies
*/ */
@import 'node_modules/@wordpress/base-styles/colors.native';
@import '@automattic/tour-kit/dist/esm/styles.scss'; @import '@automattic/tour-kit/dist/esm/styles.scss';
/** /**
@ -56,5 +55,6 @@
@import 'tour-kit/style.scss'; @import 'tour-kit/style.scss';
@import 'collapsible-content/style.scss'; @import 'collapsible-content/style.scss';
@import 'form/style.scss'; @import 'form/style.scss';
@import 'product-section-layout/style.scss';
@import 'experimental-tree-control/tree.scss'; @import 'experimental-tree-control/tree.scss';
@import 'product-section-layout/style.scss';
@import 'tree-select-control/index.scss';

View File

@ -27,13 +27,14 @@ import TablePlaceholder from './placeholder';
import TableSummary, { TableSummaryPlaceholder } from './summary'; import TableSummary, { TableSummaryPlaceholder } from './summary';
import { TableCardProps } from './types'; import { TableCardProps } from './types';
const defaultOnQueryChange = const defaultOnQueryChange: (
( param: string ) => ( path?: string, direction?: string ) => {}; param: string
) => ( path?: string, direction?: string ) => void = () => () => {};
const defaultOnColumnsChange = ( const defaultOnColumnsChange: (
showCols: Array< string >, showCols: Array< string >,
key?: string key?: string
) => {}; ) => void = () => {};
/** /**
* This is an accessible, sortable, and scrollable table for displaying tabular data (like revenue and other analytics data). * This is an accessible, sortable, and scrollable table for displaying tabular data (like revenue and other analytics data).
* It accepts `headers` for column headers, and `rows` for the table content. * It accepts `headers` for column headers, and `rows` for the table content.

View File

@ -20,6 +20,7 @@ import { BACKSPACE } from './constants';
* @param {string} props.instanceId Id of the component * @param {string} props.instanceId Id of the component
* @param {string} props.placeholder Placeholder of the search input * @param {string} props.placeholder Placeholder of the search input
* @param {boolean} props.isExpanded True if the tree is expanded * @param {boolean} props.isExpanded True if the tree is expanded
* @param {boolean} props.alwaysShowPlaceholder Will always show placeholder (default: false)
* @param {boolean} props.disabled True if the component is disabled * @param {boolean} props.disabled True if the component is disabled
* @param {number} props.maxVisibleTags The maximum number of tags to show. Undefined, 0 or less than 0 evaluates to "Show All". * @param {number} props.maxVisibleTags The maximum number of tags to show. Undefined, 0 or less than 0 evaluates to "Show All".
* @param {string} props.value The current input value * @param {string} props.value The current input value
@ -43,11 +44,14 @@ const Control = forwardRef(
onTagsChange = () => {}, onTagsChange = () => {},
onInputChange = () => {}, onInputChange = () => {},
onControlClick = noop, onControlClick = noop,
alwaysShowPlaceholder = false,
}, },
ref ref
) => { ) => {
const hasTags = tags.length > 0; const hasTags = tags.length > 0;
const showPlaceholder = ! hasTags && ! isExpanded; const showPlaceholder = alwaysShowPlaceholder
? true
: ! hasTags && ! isExpanded;
/** /**
* Handles keydown event * Handles keydown event

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