Merge branch 'trunk' into fix/tax_lookup_and_order_stat_deletion
This commit is contained in:
commit
a203681b96
33
.codecov.yml
33
.codecov.yml
|
@ -2,20 +2,34 @@ 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:
|
status:
|
||||||
project:
|
project:
|
||||||
default:
|
default:
|
||||||
informational: true
|
target: auto
|
||||||
patch:
|
patch:
|
||||||
default:
|
default:
|
||||||
informational: true
|
target: auto
|
||||||
changes: off
|
|
||||||
|
|
||||||
parsers:
|
parsers:
|
||||||
gcov:
|
gcov:
|
||||||
branch_detection:
|
branch_detection:
|
||||||
|
@ -24,4 +38,9 @@ parsers:
|
||||||
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
|
||||||
|
|
|
@ -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] -->
|
||||||
|
|
||||||
|
|
|
@ -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/**/*
|
||||||
|
|
|
@ -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,7 +14,7 @@ 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
|
||||||
|
|
|
@ -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: {}
|
||||||
|
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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
|
|
@ -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 }}
|
||||||
|
@ -25,10 +24,15 @@ jobs:
|
||||||
env:
|
env:
|
||||||
ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-results
|
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
|
ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-report
|
||||||
|
BASE_URL: ${{ secrets.SMOKE_TEST_URL }}
|
||||||
|
ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
|
||||||
|
ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
|
||||||
|
ADMIN_USER_EMAIL: ${{ secrets.SMOKE_TEST_ADMIN_USER_EMAIL }}
|
||||||
|
CUSTOMER_USER: ${{ secrets.SMOKE_TEST_CUSTOMER_USER }}
|
||||||
|
CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_CUSTOMER_PASSWORD }}
|
||||||
|
DEFAULT_TIMEOUT_OVERRIDE: 120000
|
||||||
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
|
||||||
|
@ -36,13 +40,21 @@ jobs:
|
||||||
install-filters: woocommerce
|
install-filters: woocommerce
|
||||||
build: false
|
build: false
|
||||||
|
|
||||||
|
- name: Download and install Chromium browser.
|
||||||
|
working-directory: plugins/woocommerce
|
||||||
|
run: pnpm exec playwright install chromium
|
||||||
|
|
||||||
|
- name: Run 'Update WooCommerce' test.
|
||||||
|
working-directory: plugins/woocommerce
|
||||||
|
env:
|
||||||
|
UPDATE_WC: nightly
|
||||||
|
run: pnpm exec playwright test --config=tests/e2e-pw/daily.playwright.config.js update-woocommerce.spec.js
|
||||||
|
|
||||||
- name: Run API tests.
|
- name: Run API tests.
|
||||||
working-directory: plugins/woocommerce
|
working-directory: plugins/woocommerce
|
||||||
env:
|
env:
|
||||||
BASE_URL: ${{ secrets.SMOKE_TEST_URL }}
|
|
||||||
USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
|
USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
|
||||||
USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
|
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
|
run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello.test.js
|
||||||
|
|
||||||
- name: Generate API Test report.
|
- name: Generate API Test report.
|
||||||
|
@ -80,8 +92,6 @@ jobs:
|
||||||
|
|
||||||
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
|
||||||
|
@ -93,13 +103,7 @@ jobs:
|
||||||
working-directory: plugins/woocommerce
|
working-directory: plugins/woocommerce
|
||||||
run: pnpm exec playwright install chromium
|
run: pnpm exec playwright install chromium
|
||||||
|
|
||||||
- name: Run 'Update WooCommerce' test.
|
- name: Run E2E tests.
|
||||||
working-directory: plugins/woocommerce
|
|
||||||
env:
|
|
||||||
UPDATE_WC: true
|
|
||||||
run: pnpm exec playwright test --config=tests/e2e-pw/daily.playwright.config.js update-woocommerce.spec.js
|
|
||||||
|
|
||||||
- name: Run the rest of E2E tests.
|
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
working-directory: plugins/woocommerce
|
working-directory: plugins/woocommerce
|
||||||
env:
|
env:
|
||||||
|
@ -132,8 +136,6 @@ jobs:
|
||||||
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
|
||||||
|
@ -153,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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello
|
||||||
|
|
||||||
|
- 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:
|
||||||
|
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: |
|
run: |
|
||||||
pnpm exec wc-e2e docker:up
|
aws s3 cp ${{ env.ALLURE_RESULTS_DIR }} \
|
||||||
pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js
|
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.API_WP_LATEST_ARTIFACT }}/allure-results \
|
||||||
pnpm exec wc-e2e test:e2e
|
--recursive \
|
||||||
pnpm exec wc-api-tests test api
|
--quiet
|
||||||
test-wp-version:
|
aws s3 cp ${{ env.ALLURE_REPORT_DIR }} \
|
||||||
name: Smoke test on L-${{ matrix.wp }} WordPress version
|
${{ 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
|
runs-on: ubuntu-20.04
|
||||||
|
needs: [get-tag, api-wp-latest]
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
strategy:
|
env:
|
||||||
matrix:
|
ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-report
|
||||||
wp: ['1', '2']
|
ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-results
|
||||||
steps:
|
steps:
|
||||||
- name: Create dirs.
|
|
||||||
run: |
|
|
||||||
mkdir -p package/woocommerce
|
|
||||||
mkdir -p tmp/woocommerce
|
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
|
||||||
path: package/woocommerce
|
|
||||||
|
|
||||||
- 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
93
.syncpackrc
93
.syncpackrc
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"filter": "^(?:react|react-dom|eslint|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",
|
||||||
|
@ -63,6 +72,88 @@
|
||||||
"**"
|
"**"
|
||||||
],
|
],
|
||||||
"pinVersion": "^8.32.0"
|
"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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
140
changelog.txt
140
changelog.txt
|
@ -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**
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: add
|
||||||
|
|
||||||
|
Added `woocommerce_widget_layered_nav_filters_start/end` hooks around layered nav filters widget
|
12
package.json
12
package.json
|
@ -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,13 +44,13 @@
|
||||||
"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.7.0",
|
"turbo": "^1.7.0",
|
||||||
"typescript": "^4.8.3",
|
"typescript": "^4.8.3",
|
||||||
"url-loader": "^1.1.2",
|
"url-loader": "^1.1.2",
|
||||||
|
@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Use syncpack to update dependencies.
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Lint fixes
|
|
@ -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.
|
|
@ -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,7 +41,7 @@
|
||||||
"@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.32.0",
|
"eslint": "^8.32.0",
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Syncpack update of @typescript/eslint dependencies.
|
|
@ -51,8 +51,8 @@
|
||||||
"@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.32.0",
|
"eslint": "^8.32.0",
|
||||||
|
|
|
@ -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: [
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Add a11y support for the Tree component
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Add custom rendering logic to the item label
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Add highlighter to the tree control
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Add selection logic to tree control component
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: add
|
||||||
|
|
||||||
|
Fix dependency versions
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Sync @wordpress package versions via syncpack.
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Adjust eslintrc for changes to eslint plugin.
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: fix
|
||||||
|
|
||||||
|
Refactor createOrderedChildren
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: fix
|
||||||
|
|
||||||
|
Fix issue were Options tab was not showing up anymore in new product management screen.
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: fix
|
||||||
|
|
||||||
|
Fix SelectControl and TreeControl styles.
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: update
|
||||||
|
|
||||||
|
Export TreeSelectControl component and add additional props: onInputChange, alwaysShowPlaceholder, includeParent.
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: update
|
||||||
|
|
||||||
|
Add deprecated message to product slot fill components
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: update
|
||||||
|
|
||||||
|
Add deprecated message to packages moved to product-editor package.
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: tweak
|
||||||
|
|
||||||
|
Small tweak to update reference to currencyContext component.
|
|
@ -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.32.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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
>
|
>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 ]
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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 )
|
||||||
);
|
);
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -23,9 +23,17 @@ export function useExpander( {
|
||||||
}
|
}
|
||||||
}, [ item, shouldItemBeExpanded ] );
|
}, [ item, shouldItemBeExpanded ] );
|
||||||
|
|
||||||
|
function onExpand() {
|
||||||
|
setExpanded( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCollapse() {
|
||||||
|
setExpanded( false );
|
||||||
|
}
|
||||||
|
|
||||||
function onToggleExpand() {
|
function onToggleExpand() {
|
||||||
setExpanded( ( prev ) => ! prev );
|
setExpanded( ( prev ) => ! prev );
|
||||||
}
|
}
|
||||||
|
|
||||||
return { isExpanded, onToggleExpand };
|
return { isExpanded, onExpand, onCollapse, onToggleExpand };
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 };
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
}
|
|
@ -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,
|
||||||
|
};
|
||||||
|
}
|
|
@ -2,17 +2,28 @@
|
||||||
* 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 { useExpander } from './use-expander';
|
||||||
|
import { useHighlighter } from './use-highlighter';
|
||||||
|
import { useKeyboard } from './use-keyboard';
|
||||||
|
import { useSelection } from './use-selection';
|
||||||
|
|
||||||
export function useTreeItem( {
|
export function useTreeItem( {
|
||||||
item,
|
item,
|
||||||
level,
|
level,
|
||||||
|
multiple,
|
||||||
|
selected,
|
||||||
|
index,
|
||||||
|
getLabel,
|
||||||
shouldItemBeExpanded,
|
shouldItemBeExpanded,
|
||||||
|
shouldItemBeHighlighted,
|
||||||
|
onSelect,
|
||||||
|
onRemove,
|
||||||
...props
|
...props
|
||||||
}: TreeItemProps ) {
|
}: TreeItemProps ) {
|
||||||
const nextLevel = level + 1;
|
const nextLevel = level + 1;
|
||||||
|
@ -22,22 +33,71 @@ export function useTreeItem( {
|
||||||
shouldItemBeExpanded,
|
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,
|
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,
|
shouldItemBeExpanded,
|
||||||
|
shouldItemBeHighlighted,
|
||||||
|
onSelect: selection.onSelectChildren,
|
||||||
|
onRemove: selection.onRemoveChildren,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,14 @@ export function useTree( {
|
||||||
ref,
|
ref,
|
||||||
items,
|
items,
|
||||||
level = 1,
|
level = 1,
|
||||||
|
role = 'tree',
|
||||||
|
multiple,
|
||||||
|
selected,
|
||||||
|
getItemLabel,
|
||||||
shouldItemBeExpanded,
|
shouldItemBeExpanded,
|
||||||
|
shouldItemBeHighlighted,
|
||||||
|
onSelect,
|
||||||
|
onRemove,
|
||||||
...props
|
...props
|
||||||
}: TreeProps ) {
|
}: TreeProps ) {
|
||||||
return {
|
return {
|
||||||
|
@ -19,10 +26,17 @@ export function useTree( {
|
||||||
items,
|
items,
|
||||||
treeProps: {
|
treeProps: {
|
||||||
...props,
|
...props,
|
||||||
|
role,
|
||||||
},
|
},
|
||||||
treeItemProps: {
|
treeItemProps: {
|
||||||
level,
|
level,
|
||||||
|
multiple,
|
||||||
|
selected,
|
||||||
|
getLabel: getItemLabel,
|
||||||
shouldItemBeExpanded,
|
shouldItemBeExpanded,
|
||||||
|
shouldItemBeHighlighted,
|
||||||
|
onSelect,
|
||||||
|
onRemove,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
|
import interpolate from '@automattic/interpolate-components';
|
||||||
import { BaseControl, TextControl } from '@wordpress/components';
|
import { BaseControl, TextControl } from '@wordpress/components';
|
||||||
import React, { createElement, useCallback, useState } from 'react';
|
import React, { createElement, useCallback, useRef, useState } from 'react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { TreeControl } from '../tree-control';
|
import { TreeControl } from '../tree-control';
|
||||||
import { Item, LinkedTree } from '../types';
|
import { Item, LinkedTree } from '../types';
|
||||||
|
import '../tree.scss';
|
||||||
|
|
||||||
const listItems: Item[] = [
|
const listItems: Item[] = [
|
||||||
{ value: '1', label: 'Technology' },
|
{ value: '1', label: 'Technology' },
|
||||||
|
@ -65,6 +67,193 @@ export const ExpandOnFilter: React.FC = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
|
@ -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,13 +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 {
|
&__expander {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.components-button {
|
.components-button {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
height: $control-size;
|
||||||
|
width: $control-size;
|
||||||
|
min-width: $control-size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { Button } from '@wordpress/components';
|
import { Button, CheckboxControl } from '@wordpress/components';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { chevronDown, chevronUp } from '@wordpress/icons';
|
import { chevronDown, chevronUp } from '@wordpress/icons';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
@ -24,6 +24,9 @@ export const TreeItem = forwardRef( function ForwardedTreeItem(
|
||||||
headingProps,
|
headingProps,
|
||||||
treeProps,
|
treeProps,
|
||||||
expander: { isExpanded, onToggleExpand },
|
expander: { isExpanded, onToggleExpand },
|
||||||
|
selection,
|
||||||
|
highlighter: { isHighlighted },
|
||||||
|
getLabel,
|
||||||
} = useTreeItem( {
|
} = useTreeItem( {
|
||||||
...props,
|
...props,
|
||||||
ref,
|
ref,
|
||||||
|
@ -34,16 +37,46 @@ 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 */ }
|
||||||
|
<label className="experimental-woocommerce-tree-item__label">
|
||||||
|
{ 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>
|
<span>{ item.data.label }</span>
|
||||||
</div>
|
) }
|
||||||
|
</label>
|
||||||
|
|
||||||
{ Boolean( item.children?.length ) && (
|
{ Boolean( item.children?.length ) && (
|
||||||
<div className="experimental-woocommerce-tree-item__expander">
|
<div className="experimental-woocommerce-tree-item__expander">
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -10,12 +10,75 @@ export interface LinkedTree {
|
||||||
children: LinkedTree[];
|
children: LinkedTree[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TreeProps = React.DetailedHTMLProps<
|
export type CheckedStatus = 'checked' | 'unchecked' | 'indeterminate';
|
||||||
|
|
||||||
|
type BaseTreeProps = {
|
||||||
|
/**
|
||||||
|
* It contians one item if `multiple` value is false or
|
||||||
|
* 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 TreeProps = BaseTreeProps &
|
||||||
|
Omit<
|
||||||
|
React.DetailedHTMLProps<
|
||||||
React.OlHTMLAttributes< HTMLOListElement >,
|
React.OlHTMLAttributes< HTMLOListElement >,
|
||||||
HTMLOListElement
|
HTMLOListElement
|
||||||
> & {
|
>,
|
||||||
|
'onSelect'
|
||||||
|
> & {
|
||||||
level?: number;
|
level?: number;
|
||||||
items: LinkedTree[];
|
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.
|
* Return if the tree item passed in should be expanded.
|
||||||
*
|
*
|
||||||
|
@ -31,16 +94,22 @@ export type TreeProps = React.DetailedHTMLProps<
|
||||||
* @see {@link LinkedTree}
|
* @see {@link LinkedTree}
|
||||||
*/
|
*/
|
||||||
shouldItemBeExpanded?( item: LinkedTree ): boolean;
|
shouldItemBeExpanded?( item: LinkedTree ): boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TreeItemProps = React.DetailedHTMLProps<
|
export type TreeItemProps = BaseTreeProps &
|
||||||
|
Omit<
|
||||||
|
React.DetailedHTMLProps<
|
||||||
React.LiHTMLAttributes< HTMLLIElement >,
|
React.LiHTMLAttributes< HTMLLIElement >,
|
||||||
HTMLLIElement
|
HTMLLIElement
|
||||||
> & {
|
>,
|
||||||
|
'onSelect'
|
||||||
|
> & {
|
||||||
level: number;
|
level: number;
|
||||||
item: LinkedTree;
|
item: LinkedTree;
|
||||||
|
index: number;
|
||||||
|
getLabel?( item: LinkedTree ): JSX.Element;
|
||||||
shouldItemBeExpanded?( item: LinkedTree ): boolean;
|
shouldItemBeExpanded?( item: LinkedTree ): boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TreeControlProps = Omit< TreeProps, 'items' | 'level' > & {
|
export type TreeControlProps = Omit< TreeProps, 'items' | 'level' > & {
|
||||||
items: Item[];
|
items: Item[];
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import * as components from '@wordpress/components';
|
||||||
|
|
||||||
|
declare module '@wordpress/components' {
|
||||||
|
declare namespace CheckboxControl {
|
||||||
|
interface Props {
|
||||||
|
indeterminate?: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -95,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';
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
||||||
|
|
|
@ -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,25 +64,24 @@ 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();
|
||||||
}
|
}
|
||||||
|
@ -85,15 +96,24 @@ export const Plugins = ( {
|
||||||
handleSuccess( response.data.activated, response );
|
handleSuccess( response.data.activated, response );
|
||||||
} )
|
} )
|
||||||
.catch( ( response ) => {
|
.catch( ( response ) => {
|
||||||
|
setHasBeenClicked( false );
|
||||||
handleErrors( response.errors, response );
|
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 }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { createElement } from '@wordpress/element';
|
||||||
|
import { Card, CardBody } from '@wordpress/components';
|
||||||
|
import deprecated from '@wordpress/deprecated';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { ProductSectionLayout } from './product-section-layout';
|
||||||
|
import { WooProductFieldItem } from '../woo-product-field-item';
|
||||||
|
|
||||||
|
type ProductFieldSectionProps = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description: string | JSX.Element;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ProductFieldSection: React.FC< ProductFieldSectionProps > = ( {
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
} ) => {
|
||||||
|
deprecated( `__experimentalProductFieldSection`, {
|
||||||
|
version: '13.0.0',
|
||||||
|
plugin: '@woocommerce/components',
|
||||||
|
hint: 'Moved to @woocommerce/product-editor package: import { __experimentalProductFieldSection } from @woocommerce/product-editor',
|
||||||
|
} );
|
||||||
|
return (
|
||||||
|
<ProductSectionLayout
|
||||||
|
title={ title }
|
||||||
|
description={ description }
|
||||||
|
className={ className }
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<CardBody>
|
||||||
|
{ children }
|
||||||
|
<WooProductFieldItem.Slot section={ id } />
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
</ProductSectionLayout>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { Children, isValidElement, createElement } from '@wordpress/element';
|
||||||
|
import deprecated from '@wordpress/deprecated';
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { FormSection } from '../form-section';
|
||||||
|
|
||||||
|
type ProductSectionLayoutProps = {
|
||||||
|
title: string;
|
||||||
|
description: string | JSX.Element;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ProductSectionLayout: React.FC< ProductSectionLayoutProps > = ( {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
} ) => {
|
||||||
|
deprecated( `__experimentalProductSectionLayout`, {
|
||||||
|
version: '13.0.0',
|
||||||
|
plugin: '@woocommerce/components',
|
||||||
|
hint: 'Moved to @woocommerce/product-editor package: import { __experimentalProductSectionLayout } from @woocommerce/product-editor',
|
||||||
|
} );
|
||||||
|
return (
|
||||||
|
<FormSection
|
||||||
|
title={ title }
|
||||||
|
description={ description }
|
||||||
|
className={ className }
|
||||||
|
>
|
||||||
|
{ Children.map( children, ( child ) => {
|
||||||
|
if ( isValidElement( child ) && child.props.onChange ) {
|
||||||
|
return (
|
||||||
|
<div className="product-field-layout">{ child }</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
} ) }
|
||||||
|
</FormSection>
|
||||||
|
);
|
||||||
|
};
|
|
@ -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 } />;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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( {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
|
||||||
import { createElement, useContext } from '@wordpress/element';
|
import { createElement, useContext } from '@wordpress/element';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,3 +56,5 @@
|
||||||
@import 'collapsible-content/style.scss';
|
@import 'collapsible-content/style.scss';
|
||||||
@import 'form/style.scss';
|
@import 'form/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';
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -23,7 +23,6 @@ import {
|
||||||
import useIsEqualRefValue from './useIsEqualRefValue';
|
import useIsEqualRefValue from './useIsEqualRefValue';
|
||||||
import Control from './control';
|
import Control from './control';
|
||||||
import Options from './options';
|
import Options from './options';
|
||||||
import './index.scss';
|
|
||||||
import { ARROW_DOWN, ARROW_UP, ENTER, ESCAPE, ROOT_VALUE } from './constants';
|
import { ARROW_DOWN, ARROW_UP, ENTER, ESCAPE, ROOT_VALUE } from './constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,11 +63,14 @@ import { ARROW_DOWN, ARROW_UP, ENTER, ESCAPE, ROOT_VALUE } from './constants';
|
||||||
* @param {string} [props.placeholder] Placeholder for the search control input
|
* @param {string} [props.placeholder] Placeholder for the search control input
|
||||||
* @param {string} [props.className] The class name for this component
|
* @param {string} [props.className] The class name for this component
|
||||||
* @param {boolean} [props.disabled] Disables the component
|
* @param {boolean} [props.disabled] Disables the component
|
||||||
|
* @param {boolean} [props.includeParent] Includes parent with selection.
|
||||||
|
* @param {boolean} [props.alwaysShowPlaceholder] Will always show placeholder (default: false)
|
||||||
* @param {Option[]} [props.options] Options to show in the component
|
* @param {Option[]} [props.options] Options to show in the component
|
||||||
* @param {string[]} [props.value] Selected values
|
* @param {string[]} [props.value] Selected values
|
||||||
* @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 {Function} [props.onChange] Callback when the selector changes
|
* @param {Function} [props.onChange] Callback when the selector changes
|
||||||
* @param {(visible: boolean) => void} [props.onDropdownVisibilityChange] Callback when the visibility of the dropdown options is changed.
|
* @param {(visible: boolean) => void} [props.onDropdownVisibilityChange] Callback when the visibility of the dropdown options is changed.
|
||||||
|
* @param {Function} [props.onInputChange] Callback when the selector changes
|
||||||
* @return {JSX.Element} The component
|
* @return {JSX.Element} The component
|
||||||
*/
|
*/
|
||||||
const TreeSelectControl = ( {
|
const TreeSelectControl = ( {
|
||||||
|
@ -84,6 +86,9 @@ const TreeSelectControl = ( {
|
||||||
maxVisibleTags,
|
maxVisibleTags,
|
||||||
onChange = () => {},
|
onChange = () => {},
|
||||||
onDropdownVisibilityChange = noop,
|
onDropdownVisibilityChange = noop,
|
||||||
|
onInputChange = noop,
|
||||||
|
includeParent = false,
|
||||||
|
alwaysShowPlaceholder = false,
|
||||||
} ) => {
|
} ) => {
|
||||||
let instanceId = useInstanceId( TreeSelectControl );
|
let instanceId = useInstanceId( TreeSelectControl );
|
||||||
instanceId = id ?? instanceId;
|
instanceId = id ?? instanceId;
|
||||||
|
@ -204,7 +209,12 @@ const TreeSelectControl = ( {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return this.children.flatMap( ( option ) => {
|
return this.children.flatMap( ( option ) => {
|
||||||
return option.hasChildren ? option.leaves : option;
|
if ( option.hasChildren ) {
|
||||||
|
return includeParent && option.value !== ROOT_VALUE
|
||||||
|
? [ option, ...option.leaves ]
|
||||||
|
: option.leaves;
|
||||||
|
}
|
||||||
|
return option;
|
||||||
} );
|
} );
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -217,6 +227,9 @@ const TreeSelectControl = ( {
|
||||||
* @return {boolean} True if checked, false otherwise.
|
* @return {boolean} True if checked, false otherwise.
|
||||||
*/
|
*/
|
||||||
get() {
|
get() {
|
||||||
|
if ( includeParent && this.value !== ROOT_VALUE ) {
|
||||||
|
return cache.selectedValues.includes( this.value );
|
||||||
|
}
|
||||||
if ( this.hasChildren ) {
|
if ( this.hasChildren ) {
|
||||||
return this.leaves.every( ( opt ) => opt.checked );
|
return this.leaves.every( ( opt ) => opt.checked );
|
||||||
}
|
}
|
||||||
|
@ -372,37 +385,29 @@ const TreeSelectControl = ( {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a change on the Tree options. Could be a click on a parent option
|
|
||||||
* or a child option
|
|
||||||
*
|
|
||||||
* @param {boolean} checked Indicates if the item should be checked
|
|
||||||
* @param {InnerOption} option The option to change
|
|
||||||
*/
|
|
||||||
const handleOptionsChange = ( checked, option ) => {
|
|
||||||
if ( option.hasChildren ) {
|
|
||||||
handleParentChange( checked, option );
|
|
||||||
} else {
|
|
||||||
handleSingleChange( checked, option );
|
|
||||||
}
|
|
||||||
|
|
||||||
setInputControlValue( '' );
|
|
||||||
if ( ! nodesExpanded.includes( option.parent ) ) {
|
|
||||||
controlRef.current.focus();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a change of a child element.
|
* Handles a change of a child element.
|
||||||
*
|
*
|
||||||
* @param {boolean} checked Indicates if the item should be checked
|
* @param {boolean} checked Indicates if the item should be checked
|
||||||
* @param {InnerOption} option The option to change
|
* @param {InnerOption} option The option to change
|
||||||
|
* @param {InnerOption} parent The options parent (could be null)
|
||||||
*/
|
*/
|
||||||
const handleSingleChange = ( checked, option ) => {
|
const handleSingleChange = ( checked, option, parent ) => {
|
||||||
const newValue = checked
|
const newValue = checked
|
||||||
? [ ...value, option.value ]
|
? [ ...value, option.value ]
|
||||||
: value.filter( ( el ) => el !== option.value );
|
: value.filter( ( el ) => el !== option.value );
|
||||||
|
if (
|
||||||
|
includeParent &&
|
||||||
|
parent &&
|
||||||
|
parent.value !== ROOT_VALUE &&
|
||||||
|
parent.children &&
|
||||||
|
parent.children.every( ( child ) =>
|
||||||
|
newValue.includes( child.value )
|
||||||
|
) &&
|
||||||
|
! newValue.includes( parent.value )
|
||||||
|
) {
|
||||||
|
newValue.push( parent.value );
|
||||||
|
}
|
||||||
onChange( newValue );
|
onChange( newValue );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -417,7 +422,9 @@ const TreeSelectControl = ( {
|
||||||
const changedValues = option.leaves
|
const changedValues = option.leaves
|
||||||
.filter( ( opt ) => opt.checked !== checked )
|
.filter( ( opt ) => opt.checked !== checked )
|
||||||
.map( ( opt ) => opt.value );
|
.map( ( opt ) => opt.value );
|
||||||
|
if ( includeParent && option.value !== ROOT_VALUE ) {
|
||||||
|
changedValues.push( option.value );
|
||||||
|
}
|
||||||
if ( checked ) {
|
if ( checked ) {
|
||||||
if ( ! option.expanded ) {
|
if ( ! option.expanded ) {
|
||||||
handleToggleExpanded( option );
|
handleToggleExpanded( option );
|
||||||
|
@ -430,6 +437,28 @@ const TreeSelectControl = ( {
|
||||||
onChange( newValue );
|
onChange( newValue );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a change on the Tree options. Could be a click on a parent option
|
||||||
|
* or a child option
|
||||||
|
*
|
||||||
|
* @param {boolean} checked Indicates if the item should be checked
|
||||||
|
* @param {InnerOption} option The option to change
|
||||||
|
* @param {InnerOption} parent The options parent (could be null)
|
||||||
|
*/
|
||||||
|
const handleOptionsChange = ( checked, option, parent ) => {
|
||||||
|
if ( option.hasChildren ) {
|
||||||
|
handleParentChange( checked, option );
|
||||||
|
} else {
|
||||||
|
handleSingleChange( checked, option, parent );
|
||||||
|
}
|
||||||
|
|
||||||
|
onInputChange( '' );
|
||||||
|
setInputControlValue( '' );
|
||||||
|
if ( ! nodesExpanded.includes( option.parent ) ) {
|
||||||
|
controlRef.current.focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a change of a Tag element. We map them to Value format.
|
* Handles a change of a Tag element. We map them to Value format.
|
||||||
*
|
*
|
||||||
|
@ -446,6 +475,7 @@ const TreeSelectControl = ( {
|
||||||
* @param {Event} e Event returned by the On Change function in the Input control
|
* @param {Event} e Event returned by the On Change function in the Input control
|
||||||
*/
|
*/
|
||||||
const handleOnInputChange = ( e ) => {
|
const handleOnInputChange = ( e ) => {
|
||||||
|
onInputChange( e.target.value );
|
||||||
setInputControlValue( e.target.value );
|
setInputControlValue( e.target.value );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -486,6 +516,7 @@ const TreeSelectControl = ( {
|
||||||
value={ inputControlValue }
|
value={ inputControlValue }
|
||||||
onTagsChange={ handleTagsChange }
|
onTagsChange={ handleTagsChange }
|
||||||
onInputChange={ handleOnInputChange }
|
onInputChange={ handleOnInputChange }
|
||||||
|
alwaysShowPlaceholder={ alwaysShowPlaceholder }
|
||||||
/>
|
/>
|
||||||
{ showTree && (
|
{ showTree && (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -22,6 +22,7 @@ import Checkbox from './checkbox';
|
||||||
*
|
*
|
||||||
* @param {Object} props Component parameters
|
* @param {Object} props Component parameters
|
||||||
* @param {InnerOption[]} props.options List of options to be rendered
|
* @param {InnerOption[]} props.options List of options to be rendered
|
||||||
|
* @param {InnerOption} props.parent Parent option
|
||||||
* @param {Function} props.onChange Callback when an option changes
|
* @param {Function} props.onChange Callback when an option changes
|
||||||
* @param {Function} [props.onExpanderClick] Callback when an expander is clicked.
|
* @param {Function} [props.onExpanderClick] Callback when an expander is clicked.
|
||||||
* @param {(option: InnerOption) => void} [props.onToggleExpanded] Callback when requesting an expander to be toggled.
|
* @param {(option: InnerOption) => void} [props.onToggleExpanded] Callback when requesting an expander to be toggled.
|
||||||
|
@ -31,6 +32,7 @@ const Options = ( {
|
||||||
onChange = () => {},
|
onChange = () => {},
|
||||||
onExpanderClick = noop,
|
onExpanderClick = noop,
|
||||||
onToggleExpanded = noop,
|
onToggleExpanded = noop,
|
||||||
|
parent = null,
|
||||||
} ) => {
|
} ) => {
|
||||||
/**
|
/**
|
||||||
* Alters the node with some keys for accessibility
|
* Alters the node with some keys for accessibility
|
||||||
|
@ -76,6 +78,7 @@ const Options = ( {
|
||||||
) }
|
) }
|
||||||
tabIndex="-1"
|
tabIndex="-1"
|
||||||
onClick={ ( e ) => {
|
onClick={ ( e ) => {
|
||||||
|
e.preventDefault();
|
||||||
onExpanderClick( e );
|
onExpanderClick( e );
|
||||||
onToggleExpanded( option );
|
onToggleExpanded( option );
|
||||||
} }
|
} }
|
||||||
|
@ -93,7 +96,7 @@ const Options = ( {
|
||||||
option={ option }
|
option={ option }
|
||||||
checked={ checked }
|
checked={ checked }
|
||||||
onChange={ ( e ) => {
|
onChange={ ( e ) => {
|
||||||
onChange( e.target.checked, option );
|
onChange( e.target.checked, option, parent );
|
||||||
} }
|
} }
|
||||||
onKeyDown={ ( e ) => {
|
onKeyDown={ ( e ) => {
|
||||||
handleKeyDown( e, option );
|
handleKeyDown( e, option );
|
||||||
|
@ -113,6 +116,7 @@ const Options = ( {
|
||||||
onChange={ onChange }
|
onChange={ onChange }
|
||||||
onExpanderClick={ onExpanderClick }
|
onExpanderClick={ onExpanderClick }
|
||||||
onToggleExpanded={ onToggleExpanded }
|
onToggleExpanded={ onToggleExpanded }
|
||||||
|
parent={ option }
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) }
|
) }
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { useState } from '@wordpress/element';
|
import { useEffect, useState } from '@wordpress/element';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -15,7 +15,7 @@ const treeSelectControlOptions = [
|
||||||
children: [
|
children: [
|
||||||
{ value: 'ES', label: 'Spain' },
|
{ value: 'ES', label: 'Spain' },
|
||||||
{ value: 'FR', label: 'France' },
|
{ value: 'FR', label: 'France' },
|
||||||
{ key: 'FR-Colonies', value: 'FR', label: 'France (Colonies)' },
|
{ key: 'FR-Colonies', value: 'FR-C', label: 'France (Colonies)' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -69,6 +69,12 @@ const treeSelectControlOptions = [
|
||||||
const Template = ( args ) => {
|
const Template = ( args ) => {
|
||||||
const [ selected, setSelected ] = useState( [ 'ES' ] );
|
const [ selected, setSelected ] = useState( [ 'ES' ] );
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
if ( args.onChange ) {
|
||||||
|
args.onChange( selected );
|
||||||
|
}
|
||||||
|
}, [ selected ] );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TreeSelectControl
|
<TreeSelectControl
|
||||||
{ ...args }
|
{ ...args }
|
||||||
|
@ -88,6 +94,13 @@ Base.args = {
|
||||||
options: treeSelectControlOptions,
|
options: treeSelectControlOptions,
|
||||||
maxVisibleTags: 3,
|
maxVisibleTags: 3,
|
||||||
selectAllLabel: 'All countries',
|
selectAllLabel: 'All countries',
|
||||||
|
includeParent: false,
|
||||||
|
alwaysShowPlaceholder: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
Base.argTypes = {
|
||||||
|
onInputChange: { action: 'onInputChange' },
|
||||||
|
onChange: { action: 'onChange' },
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -110,4 +110,36 @@ describe( 'TreeSelectControl - Control Component', () => {
|
||||||
placeholder = input.getAttribute( 'placeholder' );
|
placeholder = input.getAttribute( 'placeholder' );
|
||||||
expect( placeholder ).toBeFalsy();
|
expect( placeholder ).toBeFalsy();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
it( 'Renders placeholder when alwaysShowPlaceholder is true with tags or expanded', () => {
|
||||||
|
const { rerender } = render(
|
||||||
|
<Control placeholder="Select" alwaysShowPlaceholder={ true } />
|
||||||
|
);
|
||||||
|
let input = screen.queryByRole( 'combobox' );
|
||||||
|
let placeholder = input.getAttribute( 'placeholder' );
|
||||||
|
expect( placeholder ).toBe( 'Select' );
|
||||||
|
|
||||||
|
rerender(
|
||||||
|
<Control
|
||||||
|
placeholder="Select"
|
||||||
|
alwaysShowPlaceholder={ true }
|
||||||
|
tags={ [ { id: 'es', label: 'Spain' } ] }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
input = screen.queryByRole( 'combobox' );
|
||||||
|
placeholder = input.getAttribute( 'placeholder' );
|
||||||
|
expect( placeholder ).toBeTruthy();
|
||||||
|
|
||||||
|
rerender(
|
||||||
|
<Control
|
||||||
|
placeholder="Select"
|
||||||
|
isExpanded={ true }
|
||||||
|
alwaysShowPlaceholder={ true }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
input = screen.queryByRole( 'combobox' );
|
||||||
|
placeholder = input.getAttribute( 'placeholder' );
|
||||||
|
expect( placeholder ).toBeTruthy();
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -71,6 +71,25 @@ describe( 'TreeSelectControl Component', () => {
|
||||||
expect( onChange ).toHaveBeenCalledWith( [ 'ES', 'AS' ] );
|
expect( onChange ).toHaveBeenCalledWith( [ 'ES', 'AS' ] );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
it( 'Should include parent in onChange value when includeParent is truthy', () => {
|
||||||
|
const onChange = jest.fn().mockName( 'onChange' );
|
||||||
|
|
||||||
|
const { queryByLabelText, queryByRole } = render(
|
||||||
|
<TreeSelectControl
|
||||||
|
options={ options }
|
||||||
|
value={ [] }
|
||||||
|
onChange={ onChange }
|
||||||
|
includeParent={ true }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const control = queryByRole( 'combobox' );
|
||||||
|
fireEvent.click( control );
|
||||||
|
const checkbox = queryByLabelText( 'Europe' );
|
||||||
|
fireEvent.click( checkbox );
|
||||||
|
expect( onChange ).toHaveBeenCalledWith( [ 'ES', 'FR', 'IT', 'EU' ] );
|
||||||
|
} );
|
||||||
|
|
||||||
it( 'Renders the label', () => {
|
it( 'Renders the label', () => {
|
||||||
const { queryByLabelText } = render(
|
const { queryByLabelText } = render(
|
||||||
<TreeSelectControl options={ options } label="Select" />
|
<TreeSelectControl options={ options } label="Select" />
|
||||||
|
@ -148,4 +167,25 @@ describe( 'TreeSelectControl Component', () => {
|
||||||
expect( queryByLabelText( 'France' ) ).toBeFalsy(); // doesn't match pain
|
expect( queryByLabelText( 'France' ) ).toBeFalsy(); // doesn't match pain
|
||||||
expect( queryByLabelText( 'Asia' ) ).toBeFalsy(); // doesn't match pain
|
expect( queryByLabelText( 'Asia' ) ).toBeFalsy(); // doesn't match pain
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
it( 'should call onInputChange when input field changed', () => {
|
||||||
|
const onInputChangeMock = jest.fn();
|
||||||
|
const { queryByRole } = render(
|
||||||
|
<TreeSelectControl
|
||||||
|
options={ options }
|
||||||
|
onInputChange={ onInputChangeMock }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const control = queryByRole( 'combobox' );
|
||||||
|
fireEvent.click( control );
|
||||||
|
fireEvent.change( control, { target: { value: 'Asi' } } );
|
||||||
|
expect( onInputChangeMock ).toHaveBeenCalledWith( 'Asi' );
|
||||||
|
|
||||||
|
fireEvent.change( control, { target: { value: 'As' } } );
|
||||||
|
expect( onInputChangeMock ).toHaveBeenCalledWith( 'As' );
|
||||||
|
|
||||||
|
fireEvent.change( control, { target: { value: 'pain' } } );
|
||||||
|
expect( onInputChangeMock ).toHaveBeenCalledWith( 'pain' );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -5,12 +5,58 @@ import { isValidElement, Fragment } from 'react';
|
||||||
import { Slot, Fill } from '@wordpress/components';
|
import { Slot, Fill } from '@wordpress/components';
|
||||||
import { cloneElement, createElement } from '@wordpress/element';
|
import { cloneElement, createElement } from '@wordpress/element';
|
||||||
|
|
||||||
|
type ChildrenProps = {
|
||||||
|
order: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object with the children and props that will be used by `cloneElement`. They will change depending on the
|
||||||
|
* type of children passed in.
|
||||||
|
*
|
||||||
|
* @param {Node} children - Node children.
|
||||||
|
* @param {number} order - Node order.
|
||||||
|
* @param {Array} props - Fill props.
|
||||||
|
* @param {Object} injectProps - Props to inject.
|
||||||
|
* @return {Object} Object with the keys: children and props.
|
||||||
|
*/
|
||||||
|
function getChildrenAndProps< T = Fill.Props, S = Record< string, unknown > >(
|
||||||
|
children: React.ReactNode,
|
||||||
|
order: number,
|
||||||
|
props: T,
|
||||||
|
injectProps?: S
|
||||||
|
) {
|
||||||
|
if ( typeof children === 'function' ) {
|
||||||
|
return {
|
||||||
|
children: children( { ...props, order, ...injectProps } ),
|
||||||
|
props: { order, ...injectProps },
|
||||||
|
};
|
||||||
|
} else if ( isValidElement( children ) ) {
|
||||||
|
// This checks whether 'children' is a react element or a standard HTML element.
|
||||||
|
if ( typeof children?.type === 'function' ) {
|
||||||
|
return {
|
||||||
|
children,
|
||||||
|
props: {
|
||||||
|
...props,
|
||||||
|
order,
|
||||||
|
...injectProps,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
children: children as React.ReactElement< ChildrenProps >,
|
||||||
|
props: { order, ...injectProps },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw Error( 'Invalid children type' );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ordered fill item.
|
* Ordered fill item.
|
||||||
*
|
*
|
||||||
* @param {Node} children - Node children.
|
* @param {Node} children - Node children.
|
||||||
* @param {number} order - Node order.
|
* @param {number} order - Node order.
|
||||||
* @param {Array} props - Fill props.
|
* @param {Array} props - Fill props.
|
||||||
|
* @param {Object} injectProps - Props to inject.
|
||||||
* @return {Node} Node.
|
* @return {Node} Node.
|
||||||
*/
|
*/
|
||||||
function createOrderedChildren< T = Fill.Props, S = Record< string, unknown > >(
|
function createOrderedChildren< T = Fill.Props, S = Record< string, unknown > >(
|
||||||
|
@ -19,15 +65,9 @@ function createOrderedChildren< T = Fill.Props, S = Record< string, unknown > >(
|
||||||
props: T,
|
props: T,
|
||||||
injectProps?: S
|
injectProps?: S
|
||||||
) {
|
) {
|
||||||
if ( typeof children === 'function' ) {
|
const { children: childrenToRender, props: propsToRender } =
|
||||||
return cloneElement( children( { ...props, order, ...injectProps } ), {
|
getChildrenAndProps( children, order, props, injectProps );
|
||||||
order,
|
return cloneElement( childrenToRender, propsToRender );
|
||||||
...injectProps,
|
|
||||||
} );
|
|
||||||
} else if ( isValidElement( children ) ) {
|
|
||||||
return cloneElement( children, { ...props, order, ...injectProps } );
|
|
||||||
}
|
|
||||||
throw Error( 'Invalid children type' );
|
|
||||||
}
|
}
|
||||||
export { createOrderedChildren };
|
export { createOrderedChildren };
|
||||||
|
|
||||||
|
|
|
@ -27,16 +27,16 @@ A Slotfill component that will allow you to add a new field to a specific sectio
|
||||||
This is the fill component. You must provide the `id` prop to identify your product field fill with a unique string. This component will accept a series of props:
|
This is the fill component. You must provide the `id` prop to identify your product field fill with a unique string. This component will accept a series of props:
|
||||||
|
|
||||||
| Prop | Type | Description |
|
| Prop | Type | Description |
|
||||||
| -------------| -------- | ------------------------------------------------------------------------------------------------------------------------ |
|
| ---------- | ------ | ------------------------------------------------------------------------------------------------ |
|
||||||
| `id` | String | A unique string to identify your fill. Used for configuiration management. |
|
| `id` | String | A unique string to identify your fill. Used for configuration management. |
|
||||||
| `section ` | String | The string used to identify the particular section where you want to render your field. |
|
| `sections` | Array | Contains an array of name and order values for which slots it should be rendered in. |
|
||||||
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
||||||
| `order` | Number | (optional) This number will dictate the order that the fields rendered by a Slot will be appear. |
|
| `order` | Number | (optional) This number will dictate the order that the fields rendered by a Slot will be appear. |
|
||||||
|
|
||||||
### WooProductFieldItem.Slot (slot)
|
### WooProductFieldItem.Slot (slot)
|
||||||
|
|
||||||
This is the slot component, and will not be used as frequently. It must also receive the required `location` prop that will be identical to the fill `location`.
|
This is the slot component. This will render all the registered fills that match the `section` prop.
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ----------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
| --------- | ------ | --------------------------------------------------------------------------------------------------- |
|
||||||
| `section` | String | Unique to the section that the Slot appears, and must be the same as the one provided to any fills. |
|
| `section` | String | Unique to the section that the Slot appears, and must be the same as the one provided to any fills. |
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Slot, Fill } from '@wordpress/components';
|
import { Slot, Fill } from '@wordpress/components';
|
||||||
import { createElement, Children, Fragment } from '@wordpress/element';
|
import { createElement, Children, Fragment } from '@wordpress/element';
|
||||||
|
import deprecated from '@wordpress/deprecated';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -29,6 +30,8 @@ type WooProductFieldFillProps = {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_FIELD_ORDER = 20;
|
||||||
|
|
||||||
const WooProductFieldFill: React.FC< WooProductFieldFillProps > = ( {
|
const WooProductFieldFill: React.FC< WooProductFieldFillProps > = ( {
|
||||||
fieldName,
|
fieldName,
|
||||||
sectionName,
|
sectionName,
|
||||||
|
@ -39,6 +42,12 @@ const WooProductFieldFill: React.FC< WooProductFieldFillProps > = ( {
|
||||||
|
|
||||||
const fieldId = `product_field/${ sectionName }/${ fieldName }`;
|
const fieldId = `product_field/${ sectionName }/${ fieldName }`;
|
||||||
|
|
||||||
|
deprecated( `__experimentalWooProductFieldItem`, {
|
||||||
|
version: '13.0.0',
|
||||||
|
plugin: '@woocommerce/components',
|
||||||
|
hint: 'Moved to @woocommerce/product-editor package: import { __experimentalWooProductFieldItem } from @woocommerce/product-editor',
|
||||||
|
} );
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
registerFill( fieldId );
|
registerFill( fieldId );
|
||||||
}, [] );
|
}, [] );
|
||||||
|
@ -75,7 +84,8 @@ export const WooProductFieldItem: React.FC< WooProductFieldItemProps > & {
|
||||||
} = ( { children, sections, id } ) => {
|
} = ( { children, sections, id } ) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ sections.map( ( { name: sectionName, order = 20 } ) => (
|
{ sections.map(
|
||||||
|
( { name: sectionName, order = DEFAULT_FIELD_ORDER } ) => (
|
||||||
<WooProductFieldFill
|
<WooProductFieldFill
|
||||||
fieldName={ id }
|
fieldName={ id }
|
||||||
sectionName={ sectionName }
|
sectionName={ sectionName }
|
||||||
|
@ -84,7 +94,8 @@ export const WooProductFieldItem: React.FC< WooProductFieldItemProps > & {
|
||||||
>
|
>
|
||||||
{ children }
|
{ children }
|
||||||
</WooProductFieldFill>
|
</WooProductFieldFill>
|
||||||
) ) }
|
)
|
||||||
|
) }
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,16 +31,16 @@ A Slotfill component that will allow you to add a new section to the product edi
|
||||||
This is the fill component. You must provide the `id` prop to identify your section fill with a unique string. This component will accept a series of props:
|
This is the fill component. You must provide the `id` prop to identify your section fill with a unique string. This component will accept a series of props:
|
||||||
|
|
||||||
| Prop | Type | Description |
|
| Prop | Type | Description |
|
||||||
| -------------| -------- | ------------------------------------------------------------------------------------------------------------------------ |
|
| ---------- | ------ | -------------------------------------------------------------------------------------------------- |
|
||||||
| `id` | String | A unique string to identify your fill. Used for configuiration management. |
|
| `id` | String | A unique string to identify your fill. Used for configuiration management. |
|
||||||
| `location` | String | The string used to identify the particular location that you want to render your section. |
|
| `tabs` | Array | Contains an array of name and order of which slots it should be rendered in. |
|
||||||
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
||||||
| `order` | Number | (optional) This number will dictate the order that the sections rendered by a Slot will be appear. |
|
| `order` | Number | (optional) This number will dictate the order that the sections rendered by a Slot will be appear. |
|
||||||
|
|
||||||
### WooProductSectionItem.Slot (slot)
|
### WooProductSectionItem.Slot (slot)
|
||||||
|
|
||||||
This is the slot component, and will not be used as frequently. It must also receive the required `location` prop that will be identical to the fill `location`.
|
This is the slot component. This will render all the registered fills that match the `tab` prop.
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ----------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
| ----- | ------ | ---------------------------------------------------------------------------------------------------- |
|
||||||
| `location` | String | Unique to the location that the Slot appears, and must be the same as the one provided to any fills. |
|
| `tab` | String | Unique to the location that the Slot appears, and must be the same as the one provided to any fills. |
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Slot, Fill } from '@wordpress/components';
|
import { Slot, Fill } from '@wordpress/components';
|
||||||
import { createElement, Fragment } from '@wordpress/element';
|
import { createElement, Fragment } from '@wordpress/element';
|
||||||
|
import deprecated from '@wordpress/deprecated';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -21,12 +22,20 @@ type WooProductSectionSlotProps = {
|
||||||
tab: string;
|
tab: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_SECTION_ORDER = 20;
|
||||||
|
|
||||||
export const WooProductSectionItem: React.FC< WooProductSectionItemProps > & {
|
export const WooProductSectionItem: React.FC< WooProductSectionItemProps > & {
|
||||||
Slot: React.FC< Slot.Props & WooProductSectionSlotProps >;
|
Slot: React.FC< Slot.Props & WooProductSectionSlotProps >;
|
||||||
} = ( { children, tabs } ) => {
|
} = ( { children, tabs } ) => {
|
||||||
|
deprecated( `__experimentalWooProductSectionItem`, {
|
||||||
|
version: '13.0.0',
|
||||||
|
plugin: '@woocommerce/components',
|
||||||
|
hint: 'Moved to @woocommerce/product-editor package: import { __experimentalWooProductSectionItem } from @woocommerce/product-editor',
|
||||||
|
} );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ tabs.map( ( { name: tabName, order: tabOrder } ) => (
|
{ tabs.map( ( { name: tabName, order: sectionOrder } ) => (
|
||||||
<Fill
|
<Fill
|
||||||
name={ `woocommerce_product_section_${ tabName }` }
|
name={ `woocommerce_product_section_${ tabName }` }
|
||||||
key={ tabName }
|
key={ tabName }
|
||||||
|
@ -34,7 +43,7 @@ export const WooProductSectionItem: React.FC< WooProductSectionItemProps > & {
|
||||||
{ ( fillProps: Fill.Props ) => {
|
{ ( fillProps: Fill.Props ) => {
|
||||||
return createOrderedChildren<
|
return createOrderedChildren<
|
||||||
Fill.Props & { tabName: string }
|
Fill.Props & { tabName: string }
|
||||||
>( children, tabOrder || 20, {
|
>( children, sectionOrder || DEFAULT_SECTION_ORDER, {
|
||||||
tabName,
|
tabName,
|
||||||
...fillProps,
|
...fillProps,
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -19,17 +19,17 @@ A Slotfill component that will allow you to add a new tab to the product editor.
|
||||||
This is the fill component. You must provide the `id` prop to identify your section fill with a unique string. This component will accept a series of props:
|
This is the fill component. You must provide the `id` prop to identify your section fill with a unique string. This component will accept a series of props:
|
||||||
|
|
||||||
| Prop | Type | Description |
|
| Prop | Type | Description |
|
||||||
| ---------- | ------ | -------------------------------------------------------------------------------------------------------------- |
|
| ----------- | ------ | -------------------------------------------------------------------------------------------------------------- |
|
||||||
| `id` | String | A unique string to identify your fill. Used for configuiration management. |
|
| `id` | String | A unique string to identify your fill. Used for configuiration management. |
|
||||||
| `location` | String | The string used to identify the particular location that you want to render your section. |
|
| `templates` | Array | Array of name and order of which template slots it should be rendered in |
|
||||||
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
| `pluginId` | String | A unique plugin ID to identify the plugin/extension that this fill is associated with. |
|
||||||
| `tabProps` | Object | An object containing tab props: name, title, className, disabled (see TabPanel.Tab from @wordpress/components) |
|
| `tabProps` | Object | An object containing tab props: name, title, className, disabled (see TabPanel.Tab from @wordpress/components) |
|
||||||
| `order` | Number | (optional) This number will dictate the order that the sections rendered by a Slot will be appear. |
|
| `order` | Number | (optional) This number will dictate the order that the sections rendered by a Slot will be appear. |
|
||||||
|
|
||||||
### WooProductTabItem.Slot (slot)
|
### WooProductTabItem.Slot (slot)
|
||||||
|
|
||||||
This is the slot component, and will not be used as frequently. It must also receive the required `location` prop that will be identical to the fill `location`.
|
This is the slot component. This will render all the registered fills that match the `template` prop.
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ---------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
| ---------- | ------ | ---------------------------------------------------------------------------------------------------- |
|
||||||
| `location` | String | Unique to the location that the Slot appears, and must be the same as the one provided to any fills. |
|
| `template` | String | Unique to the location that the Slot appears, and must be the same as the one provided to any fills. |
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import React, { ReactElement, ReactNode } from 'react';
|
import React, { ReactElement, ReactNode } from 'react';
|
||||||
import { Slot, Fill, TabPanel } from '@wordpress/components';
|
import { Slot, Fill, TabPanel } from '@wordpress/components';
|
||||||
import { createElement, Fragment } from '@wordpress/element';
|
import { createElement, Fragment } from '@wordpress/element';
|
||||||
|
import deprecated from '@wordpress/deprecated';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -30,11 +31,19 @@ type WooProductFieldSlotProps = {
|
||||||
) => ReactElement | null;
|
) => ReactElement | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_TAB_ORDER = 20;
|
||||||
|
|
||||||
export const WooProductTabItem: React.FC< WooProductTabItemProps > & {
|
export const WooProductTabItem: React.FC< WooProductTabItemProps > & {
|
||||||
Slot: React.VFC<
|
Slot: React.VFC<
|
||||||
Omit< Slot.Props, 'children' > & WooProductFieldSlotProps
|
Omit< Slot.Props, 'children' > & WooProductFieldSlotProps
|
||||||
>;
|
>;
|
||||||
} = ( { children, tabProps, templates } ) => {
|
} = ( { children, tabProps, templates } ) => {
|
||||||
|
deprecated( `__experimentalWooProductTabItem`, {
|
||||||
|
version: '13.0.0',
|
||||||
|
plugin: '@woocommerce/components',
|
||||||
|
hint: 'Moved to @woocommerce/product-editor package: import { __experimentalWooProductTabItem } from @woocommerce/product-editor',
|
||||||
|
} );
|
||||||
|
|
||||||
if ( ! templates ) {
|
if ( ! templates ) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.warn( 'WooProductTabItem fill is missing templates property.' );
|
console.warn( 'WooProductTabItem fill is missing templates property.' );
|
||||||
|
@ -50,12 +59,12 @@ export const WooProductTabItem: React.FC< WooProductTabItemProps > & {
|
||||||
{ ( fillProps: Fill.Props ) => {
|
{ ( fillProps: Fill.Props ) => {
|
||||||
return createOrderedChildren< Fill.Props >(
|
return createOrderedChildren< Fill.Props >(
|
||||||
children,
|
children,
|
||||||
templateData.order || 20,
|
templateData.order || DEFAULT_TAB_ORDER,
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
tabProps,
|
tabProps,
|
||||||
templateName: templateData.name,
|
templateName: templateData.name,
|
||||||
order: templateData.order || 20,
|
order: templateData.order || DEFAULT_TAB_ORDER,
|
||||||
...fillProps,
|
...fillProps,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -84,7 +93,7 @@ WooProductTabItem.Slot = ( { fillProps, template, children } ) => (
|
||||||
: props.tabProps;
|
: props.tabProps;
|
||||||
tabs.push( {
|
tabs.push( {
|
||||||
...tabProps,
|
...tabProps,
|
||||||
order: props.order ?? 20,
|
order: props.order ?? DEFAULT_TAB_ORDER,
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.0.2](https://www.npmjs.com/package/@woocommerce/create-woo-extension/v/1.0.2) - 2023-02-13
|
||||||
|
|
||||||
|
- Patch - Add examples of Woo components package [#36732]
|
||||||
|
- Patch - bump WooCommerce version [#36732]
|
||||||
|
- Patch - Update readme [#36732]
|
||||||
|
|
||||||
## [1.0.1](https://www.npmjs.com/package/@woocommerce/create-woo-extension/v/1.0.1) - 2022-12-20
|
## [1.0.1](https://www.npmjs.com/package/@woocommerce/create-woo-extension/v/1.0.1) - 2022-12-20
|
||||||
|
|
||||||
- Patch - Fix install scripts [#34385]
|
- Patch - Fix install scripts [#34385]
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
Significance: patch
|
|
||||||
Type: add
|
|
||||||
|
|
||||||
Add examples of Woo components package
|
|
|
@ -1,3 +0,0 @@
|
||||||
Significance: patch
|
|
||||||
Type: dev
|
|
||||||
Comment: Just some PHP clean up to adhere to coding standards
|
|
|
@ -1,5 +0,0 @@
|
||||||
Significance: patch
|
|
||||||
Type: fix
|
|
||||||
Comment: Dev dependency update.
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
Significance: patch
|
|
||||||
Type: dev
|
|
||||||
|
|
||||||
Update readme
|
|
|
@ -1,4 +0,0 @@
|
||||||
Significance: patch
|
|
||||||
Type: update
|
|
||||||
|
|
||||||
bump WooCommerce version
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@woocommerce/create-woo-extension",
|
"name": "@woocommerce/create-woo-extension",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"description": "A template to be used with `@wordpress/create-block` to create a WooCommerce extension.",
|
"description": "A template to be used with `@wordpress/create-block` to create a WooCommerce extension.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Sync @wordpress package versions via syncpack.
|
|
@ -1,4 +1,4 @@
|
||||||
Significance: minor
|
Significance: minor
|
||||||
Type: update
|
Type: update
|
||||||
|
|
||||||
Add default value for backorders
|
Adding currencyContext component.
|
|
@ -27,10 +27,11 @@
|
||||||
"react-native": "src/index",
|
"react-native": "src/index",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@woocommerce/number": "workspace:*",
|
"@woocommerce/number": "workspace:*",
|
||||||
"@wordpress/deprecated": "^2.12.3",
|
"@wordpress/deprecated": "wp-6.0",
|
||||||
"@wordpress/element": "^4.1.1",
|
"@wordpress/element": "wp-6.0",
|
||||||
"@wordpress/html-entities": "^3.3.1",
|
"@wordpress/hooks": "wp-6.0",
|
||||||
"@wordpress/i18n": "^3.20.0"
|
"@wordpress/html-entities": "wp-6.0",
|
||||||
|
"@wordpress/i18n": "wp-6.0"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|
|
@ -3,14 +3,15 @@
|
||||||
*/
|
*/
|
||||||
import { createContext } from '@wordpress/element';
|
import { createContext } from '@wordpress/element';
|
||||||
import { applyFilters } from '@wordpress/hooks';
|
import { applyFilters } from '@wordpress/hooks';
|
||||||
import CurrencyFactory from '@woocommerce/currency';
|
import { getSetting } from '@woocommerce/settings';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { CURRENCY } from '~/utils/admin-settings';
|
import { CurrencyFactory } from './index';
|
||||||
|
|
||||||
|
const CURRENCY = getSetting( 'currency' );
|
||||||
const appCurrency = CurrencyFactory( CURRENCY );
|
const appCurrency = CurrencyFactory( CURRENCY );
|
||||||
|
|
||||||
export const getFilteredCurrencyInstance = ( query ) => {
|
export const getFilteredCurrencyInstance = ( query ) => {
|
||||||
const config = appCurrency.getCurrencyConfig();
|
const config = appCurrency.getCurrencyConfig();
|
||||||
/**
|
/**
|
|
@ -0,0 +1,9 @@
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { CurrencyFactory } from './utils';
|
||||||
|
|
||||||
|
export default CurrencyFactory;
|
||||||
|
|
||||||
|
export * from './utils';
|
||||||
|
export * from './currency-context';
|
|
@ -62,7 +62,7 @@ export type CountryInfo = {
|
||||||
* @param {CurrencyConfig} currencySetting
|
* @param {CurrencyConfig} currencySetting
|
||||||
* @return {Object} currency object
|
* @return {Object} currency object
|
||||||
*/
|
*/
|
||||||
const CurrencyFactory = function ( currencySetting?: CurrencyConfig ) {
|
const CurrencyFactoryBase = function ( currencySetting?: CurrencyConfig ) {
|
||||||
let currency: Currency;
|
let currency: Currency;
|
||||||
|
|
||||||
function stripTags( str: string ) {
|
function stripTags( str: string ) {
|
||||||
|
@ -272,7 +272,7 @@ const CurrencyFactory = function ( currencySetting?: CurrencyConfig ) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CurrencyFactory;
|
export const CurrencyFactory = CurrencyFactoryBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns currency data by country/region. Contains code, symbol, position, thousands separator, decimal separator, and precision.
|
* Returns currency data by country/region. Contains code, symbol, position, thousands separator, decimal separator, and precision.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue