Merge branch 'trunk' into feature/34909-marketing-create-campaign-modal
This commit is contained in:
commit
e653a4ca15
|
@ -3,7 +3,7 @@ on:
|
|||
workflow_dispatch:
|
||||
inputs:
|
||||
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
|
||||
default: '-a'
|
||||
|
||||
|
|
|
@ -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 }}
|
||||
E2E_ARTIFACT: e2e-daily--run-${{ github.run_number }}
|
||||
FORCE_COLOR: 1
|
||||
BRANCH_NAME: ${{ github.ref_name }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
@ -25,10 +24,15 @@ jobs:
|
|||
env:
|
||||
ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-results
|
||||
ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-report
|
||||
BASE_URL: ${{ secrets.SMOKE_TEST_URL }}
|
||||
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:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ env.BRANCH_NAME }}
|
||||
|
||||
- name: Setup WooCommerce Monorepo
|
||||
uses: ./.github/actions/setup-woocommerce-monorepo
|
||||
|
@ -36,13 +40,21 @@ jobs:
|
|||
install-filters: woocommerce
|
||||
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.
|
||||
working-directory: plugins/woocommerce
|
||||
env:
|
||||
BASE_URL: ${{ secrets.SMOKE_TEST_URL }}
|
||||
USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
|
||||
USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
|
||||
DEFAULT_TIMEOUT_OVERRIDE: 120000
|
||||
run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello.test.js
|
||||
|
||||
- name: Generate API Test report.
|
||||
|
@ -80,8 +92,6 @@ jobs:
|
|||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ env.BRANCH_NAME }}
|
||||
|
||||
- name: Setup WooCommerce Monorepo
|
||||
uses: ./.github/actions/setup-woocommerce-monorepo
|
||||
|
@ -93,13 +103,7 @@ jobs:
|
|||
working-directory: plugins/woocommerce
|
||||
run: pnpm exec playwright install chromium
|
||||
|
||||
- name: Run 'Update WooCommerce' test.
|
||||
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.
|
||||
- name: Run E2E tests.
|
||||
timeout-minutes: 60
|
||||
working-directory: plugins/woocommerce
|
||||
env:
|
||||
|
@ -132,8 +136,6 @@ jobs:
|
|||
if: success() || failure()
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ env.BRANCH_NAME }}
|
||||
|
||||
- name: Setup WooCommerce Monorepo
|
||||
uses: ./.github/actions/setup-woocommerce-monorepo
|
||||
|
@ -153,7 +155,7 @@ jobs:
|
|||
ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_PERF_ADMIN_PASSWORD }}
|
||||
CUSTOMER_USER: ${{ secrets.SMOKE_TEST_PERF_ADMIN_USER }}
|
||||
CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_PERF_ADMIN_PASSWORD }}
|
||||
UPDATE_WC: true
|
||||
UPDATE_WC: nightly
|
||||
DEFAULT_TIMEOUT_OVERRIDE: 120000
|
||||
run: |
|
||||
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'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ env.BRANCH_NAME }}
|
||||
|
||||
- name: Setup WooCommerce Monorepo
|
||||
uses: ./.github/actions/setup-woocommerce-monorepo
|
||||
|
@ -269,7 +269,6 @@ jobs:
|
|||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: repo
|
||||
ref: ${{ env.BRANCH_NAME }}
|
||||
|
||||
- name: Download API test report artifact
|
||||
uses: actions/download-artifact@v3
|
||||
|
|
|
@ -1,185 +1,691 @@
|
|||
name: Smoke test release
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_id:
|
||||
description: 'WooCommerce Release Id'
|
||||
tag:
|
||||
description: 'WooCommerce Release Tag'
|
||||
required: true
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
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:
|
||||
login-run:
|
||||
name: Daily smoke test on release.
|
||||
runs-on: ubuntu-20.04
|
||||
get-tag:
|
||||
name: Get WooCommerce release tag
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-20.04
|
||||
outputs:
|
||||
tag: ${{ steps.tag.outputs.result }}
|
||||
created: ${{ steps.created-at.outputs.created }}
|
||||
steps:
|
||||
- name: Validate tag
|
||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh release view "${{ github.event.inputs.tag }}" --repo=woocommerce/woocommerce
|
||||
|
||||
- name: Get tag
|
||||
uses: actions/github-script@v6
|
||||
id: tag
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
console.log( "${{ github.event_name }}" );
|
||||
return "${{ github.event.release.tag_name }}" || "${{ github.event.inputs.tag }}"
|
||||
|
||||
- name: Verify woocommerce.zip asset
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
RELEASE_TAG: ${{ steps.tag.outputs.result }}
|
||||
run: |
|
||||
gh release download $RELEASE_TAG --repo woocommerce/woocommerce
|
||||
if [[ -f "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: ${{ github.token }}
|
||||
run: echo "created=$(gh release view ${{ steps.tag.outputs.result }} --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:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: trunk
|
||||
|
||||
- name: Setup WooCommerce Monorepo
|
||||
uses: ./.github/actions/setup-woocommerce-monorepo
|
||||
with:
|
||||
build-filters: woocommerce
|
||||
install-filters: woocommerce
|
||||
build: false
|
||||
|
||||
- name: Install Jest
|
||||
run: npm install -g jest
|
||||
- name: Download and install Chromium browser.
|
||||
working-directory: plugins/woocommerce
|
||||
run: pnpm exec playwright install chromium
|
||||
|
||||
- name: Run smoke test.
|
||||
- name: Run 'Update WooCommerce' test.
|
||||
working-directory: plugins/woocommerce
|
||||
env:
|
||||
SMOKE_TEST_URL: ${{ secrets.RELEASE_TEST_URL }}
|
||||
SMOKE_TEST_ADMIN_USER: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
|
||||
SMOKE_TEST_ADMIN_PASSWORD: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }}
|
||||
SMOKE_TEST_ADMIN_USER_EMAIL: ${{ secrets.RELEASE_TEST_ADMIN_USER_EMAIL }}
|
||||
SMOKE_TEST_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
|
||||
ADMIN_PASSWORD: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }}
|
||||
ADMIN_USER: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
|
||||
BASE_URL: ${{ secrets.RELEASE_TEST_URL }}
|
||||
CUSTOMER_PASSWORD: ${{ secrets.RELEASE_TEST_CUSTOMER_PASSWORD }}
|
||||
CUSTOMER_USER: ${{ secrets.RELEASE_TEST_CUSTOMER_USER }}
|
||||
DEFAULT_TIMEOUT_OVERRIDE: 120000
|
||||
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
|
||||
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 }}
|
||||
USER_KEY: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
|
||||
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
|
||||
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: |
|
||||
pnpm exec wc-e2e docker:up
|
||||
pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js
|
||||
pnpm exec wc-e2e test:e2e
|
||||
pnpm exec wc-api-tests test api
|
||||
test-wp-version:
|
||||
name: Smoke test on L-${{ matrix.wp }} WordPress version
|
||||
aws s3 cp ${{ env.ALLURE_RESULTS_DIR }} \
|
||||
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.API_WP_LATEST_ARTIFACT }}/allure-results \
|
||||
--recursive \
|
||||
--quiet
|
||||
aws s3 cp ${{ env.ALLURE_REPORT_DIR }} \
|
||||
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.API_WP_LATEST_ARTIFACT }}/allure-report \
|
||||
--recursive \
|
||||
--quiet
|
||||
|
||||
- name: Publish API Allure report
|
||||
if: success() || failure()
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
|
||||
ENV_DESCRIPTION: wp-latest
|
||||
run: |
|
||||
gh workflow run publish-test-reports-release.yml \
|
||||
-f created_at="${{ needs.get-tag.outputs.created }}" \
|
||||
-f run_id=${{ github.run_id }} \
|
||||
-f run_number=${{ github.run_number }} \
|
||||
-f release_tag=${{ needs.get-tag.outputs.tag }} \
|
||||
-f artifact="${{ env.API_WP_LATEST_ARTIFACT }}" \
|
||||
-f env_description="${{ env.ENV_DESCRIPTION }}" \
|
||||
-f test_type="api" \
|
||||
--repo woocommerce/woocommerce-test-reports
|
||||
|
||||
- name: Archive API test report
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.API_WP_LATEST_ARTIFACT }}
|
||||
path: |
|
||||
${{ env.ALLURE_RESULTS_DIR }}
|
||||
${{ env.ALLURE_REPORT_DIR }}
|
||||
if-no-files-found: ignore
|
||||
retention-days: 5
|
||||
|
||||
e2e-wp-latest:
|
||||
name: E2E on WP Latest
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [get-tag, api-wp-latest]
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
matrix:
|
||||
wp: ['1', '2']
|
||||
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:
|
||||
- name: Create dirs.
|
||||
run: |
|
||||
mkdir -p package/woocommerce
|
||||
mkdir -p tmp/woocommerce
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
path: package/woocommerce
|
||||
|
||||
- name: Setup WooCommerce Monorepo
|
||||
uses: ./.github/actions/setup-woocommerce-monorepo
|
||||
with:
|
||||
build-filters: woocommerce
|
||||
install-filters: woocommerce
|
||||
build: false
|
||||
|
||||
- name: Fetch Asset ID
|
||||
id: fetch_asset_id
|
||||
uses: actions/github-script@v5
|
||||
- name: Download and install Chromium browser.
|
||||
working-directory: plugins/woocommerce
|
||||
run: pnpm exec playwright install chromium
|
||||
|
||||
- name: Run E2E tests
|
||||
env:
|
||||
RELEASE_ID: ${{ github.event.inputs.release_id }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
ADMIN_PASSWORD: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }}
|
||||
ADMIN_USER: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
|
||||
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:
|
||||
script: |
|
||||
const script = require( './package/woocommerce/.github/workflows/scripts/fetch-asset-id.js' )
|
||||
await script({github, context, core})
|
||||
name: ${{ env.E2E_UPDATE_WC_ARTIFACT }}
|
||||
path: plugins/woocommerce/tmp
|
||||
|
||||
- name: Download WooCommerce release zip
|
||||
working-directory: tmp
|
||||
- name: Add allure-results from 'e2e-update-wc'
|
||||
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
|
||||
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: |
|
||||
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
|
||||
rsync -a woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
|
||||
|
||||
- name: Load docker images and start containers.
|
||||
working-directory: package/woocommerce
|
||||
- name: Publish E2E Allure report
|
||||
if: success() || failure()
|
||||
env:
|
||||
LATEST_WP_VERSION_MINUS: ${{ matrix.wp }}
|
||||
run: pnpm docker:up --filter=woocommerce
|
||||
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: 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 }}
|
||||
run: pnpm exec wc-e2e test:e2e
|
||||
- name: Archive E2E test report
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.E2E_WP_LATEST_ARTIFACT }}
|
||||
path: |
|
||||
${{ env.ALLURE_RESULTS_DIR }}
|
||||
${{ env.ALLURE_REPORT_DIR }}
|
||||
if-no-files-found: ignore
|
||||
retention-days: 5
|
||||
|
||||
test-plugins:
|
||||
name: Smoke tests with ${{ matrix.plugin }} plugin installed
|
||||
get-wp-versions:
|
||||
name: Get WP L-1 & L-2 version numbers
|
||||
needs: [get-tag]
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- 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'
|
||||
outputs:
|
||||
matrix: ${{ steps.get-versions.outputs.versions }}
|
||||
tag: ${{ needs.get-tag.outputs.tag }}
|
||||
created: ${{ needs.get-tag.outputs.created }}
|
||||
steps:
|
||||
- name: Create dirs.
|
||||
- name: Create dirs
|
||||
run: |
|
||||
mkdir -p package/woocommerce
|
||||
mkdir -p tmp/woocommerce
|
||||
mkdir script
|
||||
mkdir repo
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
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
|
||||
uses: ./.github/actions/setup-woocommerce-monorepo
|
||||
with:
|
||||
build-filters: woocommerce
|
||||
|
||||
- name: Fetch Asset ID
|
||||
id: fetch_asset_id
|
||||
uses: actions/github-script@v5
|
||||
- name: Launch WP Env
|
||||
working-directory: plugins/woocommerce
|
||||
run: pnpm run env:test
|
||||
|
||||
- name: Download release zip
|
||||
env:
|
||||
RELEASE_ID: ${{ github.event.inputs.release_id }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
with:
|
||||
script: |
|
||||
const script = require( './package/woocommerce/.github/workflows/scripts/fetch-asset-id.js' )
|
||||
await script({github, context, core})
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: gh release download ${{ needs.get-wp-versions.outputs.tag }} --dir tmp
|
||||
|
||||
- name: Download WooCommerce release zip
|
||||
working-directory: tmp
|
||||
- name: Replace `plugins/woocommerce` with unzipped woocommerce release build
|
||||
run: unzip -d plugins -o tmp/woocommerce.zip
|
||||
|
||||
- name: Downgrade WordPress version to ${{ matrix.version.number }}
|
||||
working-directory: plugins/woocommerce
|
||||
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
|
||||
rsync -a woocommerce/woocommerce/* ../package/woocommerce/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 }}
|
||||
- name: Verify environment details
|
||||
working-directory: plugins/woocommerce
|
||||
run: |
|
||||
pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/upload-plugin.js
|
||||
pnpm exec wc-e2e test:e2e
|
||||
pnpm exec wp-env run tests-cli "wp core version"
|
||||
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
|
||||
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:
|
||||
GITHUB_TOKEN: ${{ github.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
|
||||
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
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
pnpm install
|
|
@ -1,5 +1,12 @@
|
|||
== 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**
|
||||
|
|
|
@ -62,4 +62,12 @@ To create a new package, add a new folder to `/packages`, containing…
|
|||
- 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.
|
||||
|
||||
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
|
||||
|
||||
Lint fixes
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { waitForElementByText, getElementByText } from '../utils/actions';
|
||||
import { waitForElementByText } from '../utils/actions';
|
||||
import { BasePage } from './BasePage';
|
||||
|
||||
type PaymentMethodWithSetupButton =
|
||||
|
@ -12,7 +12,7 @@ type PaymentMethodWithSetupButton =
|
|||
| 'mollie'
|
||||
| 'bacs';
|
||||
|
||||
type PaymentMethod = PaymentMethodWithSetupButton | 'cod';
|
||||
// type PaymentMethod = PaymentMethodWithSetupButton | 'cod';
|
||||
|
||||
export class PaymentsSetup extends BasePage {
|
||||
url = 'wp-admin/admin.php?page=wc-admin&task=payments';
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
module.exports = {
|
||||
extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
|
||||
root: true,
|
||||
ignorePatterns: [ '**/test/*.ts', '**/test/*.tsx' ],
|
||||
overrides: [
|
||||
{
|
||||
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: dev
|
||||
|
||||
Adjust eslintrc for changes to eslint plugin.
|
|
@ -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: tweak
|
||||
|
||||
Small tweak to update reference to currencyContext component.
|
|
@ -48,8 +48,8 @@
|
|||
"@wordpress/block-editor": "^9.8.0",
|
||||
"@wordpress/block-library": "^7.16.0",
|
||||
"@wordpress/blocks": "^11.18.0",
|
||||
"@wordpress/components": "^19.5.0",
|
||||
"@wordpress/compose": "^5.1.2",
|
||||
"@wordpress/components": "19.8.5",
|
||||
"@wordpress/compose": "5.4.1",
|
||||
"@wordpress/core-data": "^4.2.1",
|
||||
"@wordpress/date": "^4.3.1",
|
||||
"@wordpress/deprecated": "^3.3.1",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exports[`AbbreviatedCard it renders correctly 1`] = `
|
||||
<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-component="Card"
|
||||
>
|
||||
|
@ -11,7 +11,7 @@ exports[`AbbreviatedCard it renders correctly 1`] = `
|
|||
class="css-mgwsf4-View-Content em57xhy0"
|
||||
>
|
||||
<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-component="CardBody"
|
||||
>
|
||||
|
|
|
@ -7,8 +7,7 @@ import { get, find, isArray } from 'lodash';
|
|||
import interpolateComponents from '@automattic/interpolate-components';
|
||||
import classnames from 'classnames';
|
||||
import { sprintf, __, _x } from '@wordpress/i18n';
|
||||
|
||||
import CurrencyFactory from '@woocommerce/currency';
|
||||
import { CurrencyFactory } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
|
|
@ -192,7 +192,7 @@ export const compareStrings = (
|
|||
) => {
|
||||
const string1 = s1.split( splitChar );
|
||||
const string2 = s2.split( splitChar );
|
||||
const diff = new Array();
|
||||
const diff = [];
|
||||
const long = s1.length > s2.length ? string1 : string2;
|
||||
for ( let x = 0; x < long.length; x++ ) {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
|
|
|
@ -8,7 +8,7 @@ import { createElement, useCallback, useState } from '@wordpress/element';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { DateTimePickerControl, defaultDateFormat } from '../';
|
||||
import { DateTimePickerControl } from '../';
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Admin/components/DateTimePickerControl',
|
||||
|
|
|
@ -23,9 +23,17 @@ export function useExpander( {
|
|||
}
|
||||
}, [ item, shouldItemBeExpanded ] );
|
||||
|
||||
function onExpand() {
|
||||
setExpanded( true );
|
||||
}
|
||||
|
||||
function onCollapse() {
|
||||
setExpanded( false );
|
||||
}
|
||||
|
||||
function onToggleExpand() {
|
||||
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
|
||||
*/
|
||||
import React from 'react';
|
||||
import { useInstanceId } from '@wordpress/compose';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { TreeItemProps } from '../types';
|
||||
import { useExpander } from './use-expander';
|
||||
import { useHighlighter } from './use-highlighter';
|
||||
import { useKeyboard } from './use-keyboard';
|
||||
import { useSelection } from './use-selection';
|
||||
|
||||
export function useTreeItem( {
|
||||
item,
|
||||
level,
|
||||
multiple,
|
||||
selected,
|
||||
index,
|
||||
getLabel,
|
||||
shouldItemBeExpanded,
|
||||
shouldItemBeHighlighted,
|
||||
onSelect,
|
||||
onRemove,
|
||||
...props
|
||||
}: TreeItemProps ) {
|
||||
const nextLevel = level + 1;
|
||||
|
@ -22,22 +33,71 @@ export function useTreeItem( {
|
|||
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 {
|
||||
item,
|
||||
level: nextLevel,
|
||||
expander,
|
||||
selection,
|
||||
highlighter,
|
||||
getLabel,
|
||||
treeItemProps: {
|
||||
...props,
|
||||
role: 'none',
|
||||
},
|
||||
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: {
|
||||
'--level': level,
|
||||
} as React.CSSProperties,
|
||||
onKeyDown,
|
||||
},
|
||||
treeProps: {
|
||||
id: subTreeId,
|
||||
items: item.children,
|
||||
level: nextLevel,
|
||||
multiple: selection.multiple,
|
||||
selected: selection.selected,
|
||||
role: 'group',
|
||||
'aria-label': item.data.label,
|
||||
getItemLabel: getLabel,
|
||||
shouldItemBeExpanded,
|
||||
shouldItemBeHighlighted,
|
||||
onSelect: selection.onSelectChildren,
|
||||
onRemove: selection.onRemoveChildren,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,7 +11,14 @@ export function useTree( {
|
|||
ref,
|
||||
items,
|
||||
level = 1,
|
||||
role = 'tree',
|
||||
multiple,
|
||||
selected,
|
||||
getItemLabel,
|
||||
shouldItemBeExpanded,
|
||||
shouldItemBeHighlighted,
|
||||
onSelect,
|
||||
onRemove,
|
||||
...props
|
||||
}: TreeProps ) {
|
||||
return {
|
||||
|
@ -19,10 +26,17 @@ export function useTree( {
|
|||
items,
|
||||
treeProps: {
|
||||
...props,
|
||||
role,
|
||||
},
|
||||
treeItemProps: {
|
||||
level,
|
||||
multiple,
|
||||
selected,
|
||||
getLabel: getItemLabel,
|
||||
shouldItemBeExpanded,
|
||||
shouldItemBeHighlighted,
|
||||
onSelect,
|
||||
onRemove,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import interpolate from '@automattic/interpolate-components';
|
||||
import { BaseControl, TextControl } from '@wordpress/components';
|
||||
import React, { createElement, useCallback, useState } from 'react';
|
||||
import React, { createElement, useCallback, useRef, useState } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { TreeControl } from '../tree-control';
|
||||
import { Item, LinkedTree } from '../types';
|
||||
import '../tree.scss';
|
||||
|
||||
const listItems: Item[] = [
|
||||
{ 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 {
|
||||
title: 'WooCommerce Admin/experimental/TreeControl',
|
||||
component: TreeControl,
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
$control-size: $gap-large;
|
||||
|
||||
.experimental-woocommerce-tree-item {
|
||||
margin: 0;
|
||||
|
||||
&--highlighted {
|
||||
> .experimental-woocommerce-tree-item__heading {
|
||||
background-color: $gray-100;
|
||||
}
|
||||
}
|
||||
|
||||
&__heading {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
gap: $gap-smaller;
|
||||
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;
|
||||
|
||||
&:hover,
|
||||
|
@ -20,6 +29,7 @@
|
|||
background-color: $gray-100;
|
||||
}
|
||||
}
|
||||
|
||||
&__label {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
|
@ -30,13 +40,50 @@
|
|||
> span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.components-base-control__field {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.components-radio-control__input {
|
||||
@include screen-reader-only();
|
||||
}
|
||||
|
||||
.components-checkbox-control__label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.components-checkbox-control__input-container {
|
||||
display: block;
|
||||
width: $control-size;
|
||||
height: $control-size;
|
||||
}
|
||||
|
||||
svg.components-checkbox-control__checked,
|
||||
svg.components-checkbox-control__indeterminate,
|
||||
.components-checkbox-control__input[type='checkbox'] {
|
||||
position: absolute;
|
||||
border-color: $gray-700;
|
||||
width: $control-size;
|
||||
height: $control-size;
|
||||
top: 0;
|
||||
left: 0;
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__expander {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.components-button {
|
||||
padding: 0;
|
||||
height: $control-size;
|
||||
width: $control-size;
|
||||
min-width: $control-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Button } from '@wordpress/components';
|
||||
import { Button, CheckboxControl } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { chevronDown, chevronUp } from '@wordpress/icons';
|
||||
import classNames from 'classnames';
|
||||
|
@ -24,6 +24,9 @@ export const TreeItem = forwardRef( function ForwardedTreeItem(
|
|||
headingProps,
|
||||
treeProps,
|
||||
expander: { isExpanded, onToggleExpand },
|
||||
selection,
|
||||
highlighter: { isHighlighted },
|
||||
getLabel,
|
||||
} = useTreeItem( {
|
||||
...props,
|
||||
ref,
|
||||
|
@ -34,16 +37,46 @@ export const TreeItem = forwardRef( function ForwardedTreeItem(
|
|||
{ ...treeItemProps }
|
||||
className={ classNames(
|
||||
treeItemProps.className,
|
||||
'experimental-woocommerce-tree-item'
|
||||
'experimental-woocommerce-tree-item',
|
||||
{
|
||||
'experimental-woocommerce-tree-item--highlighted':
|
||||
isHighlighted,
|
||||
}
|
||||
) }
|
||||
>
|
||||
<div
|
||||
{ ...headingProps }
|
||||
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>
|
||||
</div>
|
||||
) }
|
||||
</label>
|
||||
|
||||
{ Boolean( item.children?.length ) && (
|
||||
<div className="experimental-woocommerce-tree-item__expander">
|
||||
|
|
|
@ -30,11 +30,12 @@ export const Tree = forwardRef( function ForwardedTree(
|
|||
`experimental-woocommerce-tree--level-${ level }`
|
||||
) }
|
||||
>
|
||||
{ items.map( ( child ) => (
|
||||
{ items.map( ( child, index ) => (
|
||||
<TreeItem
|
||||
{ ...treeItemProps }
|
||||
key={ child.data.value }
|
||||
item={ child }
|
||||
index={ index }
|
||||
/>
|
||||
) ) }
|
||||
</ol>
|
||||
|
|
|
@ -10,12 +10,75 @@ export interface 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 >,
|
||||
HTMLOListElement
|
||||
> & {
|
||||
>,
|
||||
'onSelect'
|
||||
> & {
|
||||
level?: number;
|
||||
items: LinkedTree[];
|
||||
/** It gives a way to render a different Element as the
|
||||
* tree item label.
|
||||
*
|
||||
* @example
|
||||
* <Tree
|
||||
* getItemLabel={ ( item ) => <span>${ item.data.label }</span> }
|
||||
* />
|
||||
*
|
||||
* @param item The current rendering tree item
|
||||
*
|
||||
* @see {@link LinkedTree}
|
||||
*/
|
||||
getItemLabel?( item: LinkedTree ): JSX.Element;
|
||||
/**
|
||||
* Return if the tree item passed in should be expanded.
|
||||
*
|
||||
|
@ -31,16 +94,22 @@ export type TreeProps = React.DetailedHTMLProps<
|
|||
* @see {@link LinkedTree}
|
||||
*/
|
||||
shouldItemBeExpanded?( item: LinkedTree ): boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type TreeItemProps = React.DetailedHTMLProps<
|
||||
export type TreeItemProps = BaseTreeProps &
|
||||
Omit<
|
||||
React.DetailedHTMLProps<
|
||||
React.LiHTMLAttributes< HTMLLIElement >,
|
||||
HTMLLIElement
|
||||
> & {
|
||||
>,
|
||||
'onSelect'
|
||||
> & {
|
||||
level: number;
|
||||
item: LinkedTree;
|
||||
index: number;
|
||||
getLabel?( item: LinkedTree ): JSX.Element;
|
||||
shouldItemBeExpanded?( item: LinkedTree ): boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type TreeControlProps = Omit< TreeProps, 'items' | 'level' > & {
|
||||
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 { updateQueryString } from '@woocommerce/navigation';
|
||||
import { getDateParamsFromQuery, getCurrentDates } from '@woocommerce/date';
|
||||
import CurrencyFactory from '@woocommerce/currency';
|
||||
import { CurrencyFactory } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
|
|
@ -95,6 +95,8 @@ export {
|
|||
SlotContextType,
|
||||
SlotContextHelpersType,
|
||||
} 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 {
|
||||
|
|
|
@ -12,11 +12,6 @@ import { useState } from '@wordpress/element';
|
|||
import { MediaUploader } from '../';
|
||||
import { File } from '../types';
|
||||
|
||||
declare let Blob: {
|
||||
prototype: Blob;
|
||||
new (): Blob;
|
||||
};
|
||||
|
||||
const MockMediaUpload = ( { onSelect, render } ) => {
|
||||
const [ isOpen, setOpen ] = useState( false );
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { createElement } from '@wordpress/element';
|
||||
import { RadioControl, SelectControl } from '@wordpress/components';
|
||||
import { SelectControl } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
|
|
@ -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 ) =>
|
||||
findIndex( selected, { id: item.id } ) !== -1;
|
||||
|
||||
|
@ -114,6 +100,20 @@ export const SearchListControl = ( props ) => {
|
|||
: filteredList;
|
||||
};
|
||||
|
||||
const onSelect = ( item ) => {
|
||||
return () => {
|
||||
if ( isSelected( item ) ) {
|
||||
onRemove( item.id )();
|
||||
return;
|
||||
}
|
||||
if ( isSingle ) {
|
||||
onChange( [ item ] );
|
||||
} else {
|
||||
onChange( [ ...selected, item ] );
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const defaultRenderItem = ( args ) => {
|
||||
return <SearchListItem { ...args } />;
|
||||
};
|
||||
|
|
|
@ -47,13 +47,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -246,13 +246,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -526,13 +526,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -710,13 +710,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -975,13 +975,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -1159,13 +1159,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -1424,13 +1424,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -1506,13 +1506,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -1669,13 +1669,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Testing search label
|
||||
|
@ -1853,13 +1853,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Testing search label
|
||||
|
@ -2118,13 +2118,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -2189,13 +2189,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -2341,13 +2341,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -2412,13 +2412,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -2564,13 +2564,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -2664,13 +2664,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -2845,13 +2845,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -2945,13 +2945,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -3176,13 +3176,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -3411,13 +3411,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -3768,13 +3768,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
@ -4045,13 +4045,13 @@ Object {
|
|||
class="woocommerce-search-list__search"
|
||||
>
|
||||
<div
|
||||
class="components-base-control css-wdf2ti-Wrapper e1puf3u4"
|
||||
class="components-base-control css-wdf2ti-Wrapper ej5x27r4"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field css-igk9ll-StyledField e1puf3u3"
|
||||
class="components-base-control__field css-10urnh1-StyledField-deprecatedMarginField ej5x27r3"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
Search for items
|
||||
|
|
|
@ -563,8 +563,8 @@ SelectControl.defaultProps = {
|
|||
autoComplete: 'off',
|
||||
};
|
||||
|
||||
export default compose( [
|
||||
export default compose(
|
||||
withSpokenMessages,
|
||||
withInstanceId,
|
||||
withFocusOutside, // this MUST be the innermost HOC as it calls handleFocusOutside
|
||||
] )( SelectControl );
|
||||
withFocusOutside // this MUST be the innermost HOC as it calls handleFocusOutside
|
||||
)( SelectControl );
|
||||
|
|
|
@ -169,7 +169,7 @@ describe( 'SelectControl', () => {
|
|||
|
||||
it( 'changes the options on search', async () => {
|
||||
const queriedOptions = [];
|
||||
// eslint-disable-next-line no-shadow
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
const queryOptions = ( options, searchedQuery ) => {
|
||||
if ( searchedQuery === 'test' ) {
|
||||
queriedOptions.push( {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { createElement, useContext } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
|
|
|
@ -57,3 +57,4 @@
|
|||
@import 'form/style.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 { TableCardProps } from './types';
|
||||
|
||||
const defaultOnQueryChange =
|
||||
( param: string ) => ( path?: string, direction?: string ) => {};
|
||||
const defaultOnQueryChange: (
|
||||
param: string
|
||||
) => ( path?: string, direction?: string ) => void = () => () => {};
|
||||
|
||||
const defaultOnColumnsChange = (
|
||||
const defaultOnColumnsChange: (
|
||||
showCols: Array< string >,
|
||||
key?: string
|
||||
) => {};
|
||||
) => void = () => {};
|
||||
/**
|
||||
* 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.
|
||||
|
|
|
@ -20,6 +20,7 @@ import { BACKSPACE } from './constants';
|
|||
* @param {string} props.instanceId Id of the component
|
||||
* @param {string} props.placeholder Placeholder of the search input
|
||||
* @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 {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
|
||||
|
@ -43,11 +44,14 @@ const Control = forwardRef(
|
|||
onTagsChange = () => {},
|
||||
onInputChange = () => {},
|
||||
onControlClick = noop,
|
||||
alwaysShowPlaceholder = false,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const hasTags = tags.length > 0;
|
||||
const showPlaceholder = ! hasTags && ! isExpanded;
|
||||
const showPlaceholder = alwaysShowPlaceholder
|
||||
? true
|
||||
: ! hasTags && ! isExpanded;
|
||||
|
||||
/**
|
||||
* Handles keydown event
|
||||
|
|
|
@ -23,7 +23,6 @@ import {
|
|||
import useIsEqualRefValue from './useIsEqualRefValue';
|
||||
import Control from './control';
|
||||
import Options from './options';
|
||||
import './index.scss';
|
||||
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.className] The class name for this 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 {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 {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 {Function} [props.onInputChange] Callback when the selector changes
|
||||
* @return {JSX.Element} The component
|
||||
*/
|
||||
const TreeSelectControl = ( {
|
||||
|
@ -84,6 +86,9 @@ const TreeSelectControl = ( {
|
|||
maxVisibleTags,
|
||||
onChange = () => {},
|
||||
onDropdownVisibilityChange = noop,
|
||||
onInputChange = noop,
|
||||
includeParent = false,
|
||||
alwaysShowPlaceholder = false,
|
||||
} ) => {
|
||||
let instanceId = useInstanceId( TreeSelectControl );
|
||||
instanceId = id ?? instanceId;
|
||||
|
@ -204,7 +209,12 @@ const TreeSelectControl = ( {
|
|||
return [];
|
||||
}
|
||||
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.
|
||||
*/
|
||||
get() {
|
||||
if ( includeParent && this.value !== ROOT_VALUE ) {
|
||||
return cache.selectedValues.includes( this.value );
|
||||
}
|
||||
if ( this.hasChildren ) {
|
||||
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.
|
||||
*
|
||||
* @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 handleSingleChange = ( checked, option ) => {
|
||||
const handleSingleChange = ( checked, option, parent ) => {
|
||||
const newValue = checked
|
||||
? [ ...value, 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 );
|
||||
};
|
||||
|
||||
|
@ -417,7 +422,9 @@ const TreeSelectControl = ( {
|
|||
const changedValues = option.leaves
|
||||
.filter( ( opt ) => opt.checked !== checked )
|
||||
.map( ( opt ) => opt.value );
|
||||
|
||||
if ( includeParent && option.value !== ROOT_VALUE ) {
|
||||
changedValues.push( option.value );
|
||||
}
|
||||
if ( checked ) {
|
||||
if ( ! option.expanded ) {
|
||||
handleToggleExpanded( option );
|
||||
|
@ -430,6 +437,28 @@ const TreeSelectControl = ( {
|
|||
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.
|
||||
*
|
||||
|
@ -446,6 +475,7 @@ const TreeSelectControl = ( {
|
|||
* @param {Event} e Event returned by the On Change function in the Input control
|
||||
*/
|
||||
const handleOnInputChange = ( e ) => {
|
||||
onInputChange( e.target.value );
|
||||
setInputControlValue( e.target.value );
|
||||
};
|
||||
|
||||
|
@ -486,6 +516,7 @@ const TreeSelectControl = ( {
|
|||
value={ inputControlValue }
|
||||
onTagsChange={ handleTagsChange }
|
||||
onInputChange={ handleOnInputChange }
|
||||
alwaysShowPlaceholder={ alwaysShowPlaceholder }
|
||||
/>
|
||||
{ showTree && (
|
||||
<div
|
||||
|
|
|
@ -22,6 +22,7 @@ import Checkbox from './checkbox';
|
|||
*
|
||||
* @param {Object} props Component parameters
|
||||
* @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.onExpanderClick] Callback when an expander is clicked.
|
||||
* @param {(option: InnerOption) => void} [props.onToggleExpanded] Callback when requesting an expander to be toggled.
|
||||
|
@ -31,6 +32,7 @@ const Options = ( {
|
|||
onChange = () => {},
|
||||
onExpanderClick = noop,
|
||||
onToggleExpanded = noop,
|
||||
parent = null,
|
||||
} ) => {
|
||||
/**
|
||||
* Alters the node with some keys for accessibility
|
||||
|
@ -76,6 +78,7 @@ const Options = ( {
|
|||
) }
|
||||
tabIndex="-1"
|
||||
onClick={ ( e ) => {
|
||||
e.preventDefault();
|
||||
onExpanderClick( e );
|
||||
onToggleExpanded( option );
|
||||
} }
|
||||
|
@ -93,7 +96,7 @@ const Options = ( {
|
|||
option={ option }
|
||||
checked={ checked }
|
||||
onChange={ ( e ) => {
|
||||
onChange( e.target.checked, option );
|
||||
onChange( e.target.checked, option, parent );
|
||||
} }
|
||||
onKeyDown={ ( e ) => {
|
||||
handleKeyDown( e, option );
|
||||
|
@ -113,6 +116,7 @@ const Options = ( {
|
|||
onChange={ onChange }
|
||||
onExpanderClick={ onExpanderClick }
|
||||
onToggleExpanded={ onToggleExpanded }
|
||||
parent={ option }
|
||||
/>
|
||||
</div>
|
||||
) }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useState } from '@wordpress/element';
|
||||
import { useEffect, useState } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -15,7 +15,7 @@ const treeSelectControlOptions = [
|
|||
children: [
|
||||
{ value: 'ES', label: 'Spain' },
|
||||
{ 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 [ selected, setSelected ] = useState( [ 'ES' ] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( args.onChange ) {
|
||||
args.onChange( selected );
|
||||
}
|
||||
}, [ selected ] );
|
||||
|
||||
return (
|
||||
<TreeSelectControl
|
||||
{ ...args }
|
||||
|
@ -88,6 +94,13 @@ Base.args = {
|
|||
options: treeSelectControlOptions,
|
||||
maxVisibleTags: 3,
|
||||
selectAllLabel: 'All countries',
|
||||
includeParent: false,
|
||||
alwaysShowPlaceholder: false,
|
||||
};
|
||||
|
||||
Base.argTypes = {
|
||||
onInputChange: { action: 'onInputChange' },
|
||||
onChange: { action: 'onChange' },
|
||||
};
|
||||
|
||||
export default {
|
||||
|
|
|
@ -110,4 +110,36 @@ describe( 'TreeSelectControl - Control Component', () => {
|
|||
placeholder = input.getAttribute( 'placeholder' );
|
||||
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' ] );
|
||||
} );
|
||||
|
||||
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', () => {
|
||||
const { queryByLabelText } = render(
|
||||
<TreeSelectControl options={ options } label="Select" />
|
||||
|
@ -148,4 +167,25 @@ describe( 'TreeSelectControl Component', () => {
|
|||
expect( queryByLabelText( 'France' ) ).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' );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Adding currencyContext component.
|
|
@ -29,6 +29,7 @@
|
|||
"@woocommerce/number": "workspace:*",
|
||||
"@wordpress/deprecated": "^2.12.3",
|
||||
"@wordpress/element": "^4.1.1",
|
||||
"@wordpress/hooks": "^3.5.0",
|
||||
"@wordpress/html-entities": "^3.3.1",
|
||||
"@wordpress/i18n": "^3.20.0"
|
||||
},
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
*/
|
||||
import { createContext } from '@wordpress/element';
|
||||
import { applyFilters } from '@wordpress/hooks';
|
||||
import CurrencyFactory from '@woocommerce/currency';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { CURRENCY } from '~/utils/admin-settings';
|
||||
import { CurrencyFactory } from './index';
|
||||
|
||||
const CURRENCY = getSetting( 'currency' );
|
||||
const appCurrency = CurrencyFactory( CURRENCY );
|
||||
|
||||
export const getFilteredCurrencyInstance = ( query ) => {
|
||||
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
|
||||
* @return {Object} currency object
|
||||
*/
|
||||
const CurrencyFactory = function ( currencySetting?: CurrencyConfig ) {
|
||||
const CurrencyFactoryBase = function ( currencySetting?: CurrencyConfig ) {
|
||||
let currency: Currency;
|
||||
|
||||
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.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: dev
|
||||
|
||||
Fix lint issues
|
|
@ -5,7 +5,6 @@ import { createElement, useState } from '@wordpress/element';
|
|||
import PropTypes from 'prop-types';
|
||||
import { Button, Modal } from '@wordpress/components';
|
||||
import { Text } from '@woocommerce/experimental';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Provides a modal requesting customer feedback.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { createElement } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { createElement } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: dev
|
||||
|
||||
Fix lint issues
|
|
@ -223,7 +223,6 @@ describe( 'products reducer', () => {
|
|||
} );
|
||||
|
||||
it( 'should handle CREATE_PRODUCT_START', () => {
|
||||
const id = 1;
|
||||
const state = reducer( defaultState, {
|
||||
type: TYPES.CREATE_PRODUCT_START,
|
||||
} );
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
module.exports = {
|
||||
extends: ['plugin:@woocommerce/eslint-plugin/recommended'],
|
||||
plugins: ['jest'],
|
||||
extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
|
||||
plugins: [ 'jest' ],
|
||||
root: true,
|
||||
env: {
|
||||
'jest/globals': true,
|
||||
},
|
||||
rules: {
|
||||
'jest/expect-expect': 'off',
|
||||
'jest/no-disabled-tests': 'off',
|
||||
'@typescript-eslint/no-shadow': 'off',
|
||||
},
|
||||
globals: {
|
||||
page: true,
|
||||
},
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
module.exports = {
|
||||
extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
|
||||
ignorePatterns: [ '**/jest.*' ],
|
||||
env: {
|
||||
'jest/globals': true,
|
||||
},
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
module.exports = {
|
||||
extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
|
||||
root: true,
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: fix
|
||||
|
||||
Update deps and fix a bug where package rc files were not respected.
|
|
@ -1 +0,0 @@
|
|||
module.exports = require( 'requireindex' )( __dirname );
|
|
@ -3,6 +3,7 @@ module.exports = {
|
|||
'plugin:react-hooks/recommended',
|
||||
require.resolve( './custom.js' ),
|
||||
'plugin:@wordpress/eslint-plugin/recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
globals: {
|
||||
|
@ -10,7 +11,7 @@ module.exports = {
|
|||
'jest/globals': true,
|
||||
jest: true,
|
||||
},
|
||||
plugins: [ '@wordpress' ],
|
||||
plugins: [ '@wordpress', '@typescript-eslint' ],
|
||||
rules: {
|
||||
radix: 'error',
|
||||
yoda: [ 'error', 'never' ],
|
||||
|
@ -22,6 +23,15 @@ module.exports = {
|
|||
allowedTextDomain: 'woocommerce',
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-explicit-any': 'error',
|
||||
'@typescript-eslint/no-use-before-define': [ 'error' ],
|
||||
'@typescript-eslint/no-shadow': [ 'error' ],
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
camelcase: 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'jsdoc/require-param': 'off',
|
||||
// Making use of typescript no-shadow instead, fixes issues with enum.
|
||||
'no-shadow': 'off',
|
||||
'@wordpress/valid-sprintf': 'warn',
|
||||
'@wordpress/no-unsafe-wp-apis': 'warn',
|
||||
'@wordpress/no-global-active-element': 'warn',
|
||||
|
@ -60,17 +70,15 @@ module.exports = {
|
|||
},
|
||||
overrides: [
|
||||
{
|
||||
files: [ '*.ts', '*.tsx' ],
|
||||
extends: [ 'plugin:@typescript-eslint/recommended' ],
|
||||
files: [ '*.js', '*.jsx' ],
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'error',
|
||||
'@typescript-eslint/no-use-before-define': [ 'error' ],
|
||||
'@typescript-eslint/no-shadow': [ 'error' ],
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
camelcase: 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'jsdoc/require-param': 'off',
|
||||
// Making use of typescript no-shadow instead, fixes issues with enum.
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [ '*.ts', '*.tsx' ],
|
||||
rules: {
|
||||
// Making use of typescript no-shadow instead.
|
||||
'no-shadow': 'off',
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
module.exports = {
|
||||
configs: require( './configs' ),
|
||||
rules: require( './rules' ),
|
||||
configs: {
|
||||
recommended: require( './configs/recommended' ),
|
||||
},
|
||||
rules: {
|
||||
'dependency-group': require( './rules/dependency-group' ),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -30,11 +30,11 @@
|
|||
],
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/parser": "^5.14.0",
|
||||
"@wordpress/eslint-plugin": "^13.3.0",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"eslint-plugin-testing-library": "^5.1.0",
|
||||
"requireindex": "^1.2.0"
|
||||
"@typescript-eslint/parser": "^5.54.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||
"@wordpress/eslint-plugin": "^14.0.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-testing-library": "^5.10.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
module.exports = require( 'requireindex' )( __dirname );
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: dev
|
||||
|
||||
Fix lint issues
|
|
@ -143,7 +143,6 @@ const InboxNoteCard: React.FC< InboxNoteProps > = ( {
|
|||
|
||||
const {
|
||||
content,
|
||||
date_created: dateCreated,
|
||||
date_created_gmt: dateCreatedGmt,
|
||||
image,
|
||||
is_deleted: isDeleted,
|
||||
|
|
|
@ -49,37 +49,6 @@ function getBuildPath( file, buildFolder ) {
|
|||
return path.resolve( pkgBuildPath, relativeToSrcPath );
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of scss and js filepaths, divide them into sets them and rebuild.
|
||||
*
|
||||
* @param {Array} files list of files to rebuild
|
||||
*/
|
||||
function buildFiles( files ) {
|
||||
// Reduce files into a unique sets of javaScript files and scss packages.
|
||||
const buildPaths = files.reduce(
|
||||
( accumulator, filePath ) => {
|
||||
if ( isJsFile( filePath ) ) {
|
||||
accumulator.jsFiles.add( filePath );
|
||||
}
|
||||
return accumulator;
|
||||
},
|
||||
{ jsFiles: new Set() }
|
||||
);
|
||||
|
||||
buildPaths.jsFiles.forEach( buildJsFile );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a javaScript file for the required environments (node and ES5)
|
||||
*
|
||||
* @param {string} file File path to build
|
||||
* @param {boolean} silent Show logs
|
||||
*/
|
||||
function buildJsFile( file, silent ) {
|
||||
buildJsFileFor( file, silent, 'main' );
|
||||
buildJsFileFor( file, silent, 'module' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a file for a specific environment
|
||||
*
|
||||
|
@ -116,6 +85,37 @@ function buildJsFileFor( file, silent, environment ) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a javaScript file for the required environments (node and ES5)
|
||||
*
|
||||
* @param {string} file File path to build
|
||||
* @param {boolean} silent Show logs
|
||||
*/
|
||||
function buildJsFile( file, silent ) {
|
||||
buildJsFileFor( file, silent, 'main' );
|
||||
buildJsFileFor( file, silent, 'module' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of scss and js filepaths, divide them into sets them and rebuild.
|
||||
*
|
||||
* @param {Array} files list of files to rebuild
|
||||
*/
|
||||
function buildFiles( files ) {
|
||||
// Reduce files into a unique sets of javaScript files and scss packages.
|
||||
const buildPaths = files.reduce(
|
||||
( accumulator, filePath ) => {
|
||||
if ( isJsFile( filePath ) ) {
|
||||
accumulator.jsFiles.add( filePath );
|
||||
}
|
||||
return accumulator;
|
||||
},
|
||||
{ jsFiles: new Set() }
|
||||
);
|
||||
|
||||
buildPaths.jsFiles.forEach( buildJsFile );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the provided package path
|
||||
*
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
"eslint": "^8.32.0",
|
||||
"jest": "^27.5.1",
|
||||
"jest-cli": "^27.5.1",
|
||||
"resize-observer-polyfill": "1.5.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"ts-jest": "^27.1.3",
|
||||
"typescript": "^4.8.3"
|
||||
|
|
|
@ -5,6 +5,10 @@ import { setLocaleData } from '@wordpress/i18n';
|
|||
import { registerStore } from '@wordpress/data';
|
||||
import 'regenerator-runtime/runtime';
|
||||
|
||||
// Due to the dependency @wordpress/compose which introduces the use of
|
||||
// ResizeObserver this global mock is required for some tests to work.
|
||||
global.ResizeObserver = require( 'resize-observer-polyfill' );
|
||||
|
||||
// Set up `wp.*` aliases. Doing this because any tests importing wp stuff will
|
||||
// likely run into this.
|
||||
global.wp = {
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: dev
|
||||
|
||||
Fix lint issues
|
|
@ -3,6 +3,20 @@
|
|||
*/
|
||||
import { find, get, omit } from 'lodash';
|
||||
|
||||
/**
|
||||
* Get the url query key from the filter key and rule.
|
||||
*
|
||||
* @param {string} key - filter key.
|
||||
* @param {string} rule - filter rule.
|
||||
* @return {string} - url query key.
|
||||
*/
|
||||
export function getUrlKey( key, rule ) {
|
||||
if ( rule && rule.length ) {
|
||||
return `${ key }_${ rule }`;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse an array of filter values with subFilters into a 1-dimensional array.
|
||||
*
|
||||
|
@ -166,17 +180,3 @@ export function getQueryFromActiveFilters( activeFilters, query, config ) {
|
|||
|
||||
return { ...previousData, ...nextData };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url query key from the filter key and rule.
|
||||
*
|
||||
* @param {string} key - filter key.
|
||||
* @param {string} rule - filter rule.
|
||||
* @return {string} - url query key.
|
||||
*/
|
||||
export function getUrlKey( key, rule ) {
|
||||
if ( rule && rule.length ) {
|
||||
return `${ key }_${ rule }`;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,41 @@ const TIME_EXCLUDED_SCREENS_FILTER = 'woocommerce_admin_time_excluded_screens';
|
|||
*/
|
||||
export const getPath = () => getHistory().location.pathname;
|
||||
|
||||
/**
|
||||
* Get the current query string, parsed into an object, from history.
|
||||
*
|
||||
* @return {Object} Current query object, defaults to empty object.
|
||||
*/
|
||||
export function getQuery() {
|
||||
const search = getHistory().location.search;
|
||||
if ( search.length ) {
|
||||
return parse( search.substring( 1 ) ) || {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a URL with set query parameters.
|
||||
*
|
||||
* @param {Object} query object of params to be updated.
|
||||
* @param {string} path Relative path (defaults to current path).
|
||||
* @param {Object} currentQuery object of current query params (defaults to current querystring).
|
||||
* @param {string} page Page key (defaults to "wc-admin")
|
||||
* @return {string} Updated URL merging query params into existing params.
|
||||
*/
|
||||
export function getNewPath(
|
||||
query,
|
||||
path = getPath(),
|
||||
currentQuery = getQuery(),
|
||||
page = 'wc-admin'
|
||||
) {
|
||||
const args = { page, ...currentQuery, ...query };
|
||||
if ( path !== '/' ) {
|
||||
args.path = path;
|
||||
}
|
||||
return addQueryArgs( 'admin.php', args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets query parameters that should persist between screens or updates
|
||||
* to reports, such as filtering.
|
||||
|
@ -75,16 +110,6 @@ export const getQueryExcludedScreens = () =>
|
|||
'homescreen',
|
||||
] );
|
||||
|
||||
/**
|
||||
* Given a path, return whether it is an excluded screen
|
||||
*
|
||||
* @param {Object} path Path to check
|
||||
*
|
||||
* @return {boolean} Boolean representing whether path is excluded
|
||||
*/
|
||||
export const pathIsExcluded = ( path ) =>
|
||||
getQueryExcludedScreens().includes( getScreenFromPath( path ) );
|
||||
|
||||
/**
|
||||
* Retrieve a string 'name' representing the current screen
|
||||
*
|
||||
|
@ -97,16 +122,6 @@ export const getScreenFromPath = ( path = getPath() ) => {
|
|||
: path.replace( '/analytics', '' ).replace( '/', '' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an array of IDs from a comma-separated query parameter.
|
||||
*
|
||||
* @param {string} [queryString=''] string value extracted from URL.
|
||||
* @return {Array<number>} List of IDs converted to an array of unique integers.
|
||||
*/
|
||||
export function getIdsFromQuery( queryString = '' ) {
|
||||
return [ ...getSetOfIdsFromQuery( queryString ) ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of IDs from a comma-separated query parameter.
|
||||
*
|
||||
|
@ -122,121 +137,6 @@ export function getSetOfIdsFromQuery( queryString = '' ) {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of searched words given a query.
|
||||
*
|
||||
* @param {Object} query Query object.
|
||||
* @return {Array} List of search words.
|
||||
*/
|
||||
export function getSearchWords( query = navUtils.getQuery() ) {
|
||||
if ( typeof query !== 'object' ) {
|
||||
throw new Error(
|
||||
'Invalid parameter passed to getSearchWords, it expects an object or no parameters.'
|
||||
);
|
||||
}
|
||||
const { search } = query;
|
||||
if ( ! search ) {
|
||||
return [];
|
||||
}
|
||||
if ( typeof search !== 'string' ) {
|
||||
throw new Error(
|
||||
"Invalid 'search' type. getSearchWords expects query's 'search' property to be a string."
|
||||
);
|
||||
}
|
||||
return search
|
||||
.split( ',' )
|
||||
.map( ( searchWord ) => searchWord.replace( '%2C', ',' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a URL with set query parameters.
|
||||
*
|
||||
* @param {Object} query object of params to be updated.
|
||||
* @param {string} path Relative path (defaults to current path).
|
||||
* @param {Object} currentQuery object of current query params (defaults to current querystring).
|
||||
* @param {string} page Page key (defaults to "wc-admin")
|
||||
* @return {string} Updated URL merging query params into existing params.
|
||||
*/
|
||||
export function getNewPath(
|
||||
query,
|
||||
path = getPath(),
|
||||
currentQuery = getQuery(),
|
||||
page = 'wc-admin'
|
||||
) {
|
||||
const args = { page, ...currentQuery, ...query };
|
||||
if ( path !== '/' ) {
|
||||
args.path = path;
|
||||
}
|
||||
return addQueryArgs( 'admin.php', args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current query string, parsed into an object, from history.
|
||||
*
|
||||
* @return {Object} Current query object, defaults to empty object.
|
||||
*/
|
||||
export function getQuery() {
|
||||
const search = getHistory().location.search;
|
||||
if ( search.length ) {
|
||||
return parse( search.substring( 1 ) ) || {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Like getQuery but in useHook format for easy usage in React functional components
|
||||
*
|
||||
* @return {Record<string, string>} Current query object, defaults to empty object.
|
||||
*/
|
||||
export const useQuery = () => {
|
||||
const [ queryState, setQueryState ] = useState( {} );
|
||||
const [ locationChanged, setLocationChanged ] = useState( true );
|
||||
useLayoutEffect( () => {
|
||||
return addHistoryListener( () => {
|
||||
setLocationChanged( true );
|
||||
} );
|
||||
}, [] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( locationChanged ) {
|
||||
const query = getQuery();
|
||||
setQueryState( query );
|
||||
setLocationChanged( false );
|
||||
}
|
||||
}, [ locationChanged ] );
|
||||
return queryState;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function returns an event handler for the given `param`
|
||||
*
|
||||
* @param {string} param The parameter in the querystring which should be updated (ex `page`, `per_page`)
|
||||
* @param {string} path Relative path (defaults to current path).
|
||||
* @param {string} query object of current query params (defaults to current querystring).
|
||||
* @return {Function} A callback which will update `param` to the passed value when called.
|
||||
*/
|
||||
export function onQueryChange( param, path = getPath(), query = getQuery() ) {
|
||||
switch ( param ) {
|
||||
case 'sort':
|
||||
return ( key, dir ) =>
|
||||
updateQueryString( { orderby: key, order: dir }, path, query );
|
||||
case 'compare':
|
||||
return ( key, queryParam, ids ) =>
|
||||
updateQueryString(
|
||||
{
|
||||
[ queryParam ]: `compare-${ key }`,
|
||||
[ key ]: ids,
|
||||
search: undefined,
|
||||
},
|
||||
path,
|
||||
query
|
||||
);
|
||||
default:
|
||||
return ( value ) =>
|
||||
updateQueryString( { [ param ]: value }, path, query );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the query parameters of the current page.
|
||||
*
|
||||
|
@ -300,6 +200,106 @@ export const addHistoryListener = ( listener ) => {
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a path, return whether it is an excluded screen
|
||||
*
|
||||
* @param {Object} path Path to check
|
||||
*
|
||||
* @return {boolean} Boolean representing whether path is excluded
|
||||
*/
|
||||
export const pathIsExcluded = ( path ) =>
|
||||
getQueryExcludedScreens().includes( getScreenFromPath( path ) );
|
||||
|
||||
/**
|
||||
* Get an array of IDs from a comma-separated query parameter.
|
||||
*
|
||||
* @param {string} [queryString=''] string value extracted from URL.
|
||||
* @return {Array<number>} List of IDs converted to an array of unique integers.
|
||||
*/
|
||||
export function getIdsFromQuery( queryString = '' ) {
|
||||
return [ ...getSetOfIdsFromQuery( queryString ) ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of searched words given a query.
|
||||
*
|
||||
* @param {Object} query Query object.
|
||||
* @return {Array} List of search words.
|
||||
*/
|
||||
export function getSearchWords( query = navUtils.getQuery() ) {
|
||||
if ( typeof query !== 'object' ) {
|
||||
throw new Error(
|
||||
'Invalid parameter passed to getSearchWords, it expects an object or no parameters.'
|
||||
);
|
||||
}
|
||||
const { search } = query;
|
||||
if ( ! search ) {
|
||||
return [];
|
||||
}
|
||||
if ( typeof search !== 'string' ) {
|
||||
throw new Error(
|
||||
"Invalid 'search' type. getSearchWords expects query's 'search' property to be a string."
|
||||
);
|
||||
}
|
||||
return search
|
||||
.split( ',' )
|
||||
.map( ( searchWord ) => searchWord.replace( '%2C', ',' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Like getQuery but in useHook format for easy usage in React functional components
|
||||
*
|
||||
* @return {Record<string, string>} Current query object, defaults to empty object.
|
||||
*/
|
||||
export const useQuery = () => {
|
||||
const [ queryState, setQueryState ] = useState( {} );
|
||||
const [ locationChanged, setLocationChanged ] = useState( true );
|
||||
useLayoutEffect( () => {
|
||||
return addHistoryListener( () => {
|
||||
setLocationChanged( true );
|
||||
} );
|
||||
}, [] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( locationChanged ) {
|
||||
const query = getQuery();
|
||||
setQueryState( query );
|
||||
setLocationChanged( false );
|
||||
}
|
||||
}, [ locationChanged ] );
|
||||
return queryState;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function returns an event handler for the given `param`
|
||||
*
|
||||
* @param {string} param The parameter in the querystring which should be updated (ex `page`, `per_page`)
|
||||
* @param {string} path Relative path (defaults to current path).
|
||||
* @param {string} query object of current query params (defaults to current querystring).
|
||||
* @return {Function} A callback which will update `param` to the passed value when called.
|
||||
*/
|
||||
export function onQueryChange( param, path = getPath(), query = getQuery() ) {
|
||||
switch ( param ) {
|
||||
case 'sort':
|
||||
return ( key, dir ) =>
|
||||
updateQueryString( { orderby: key, order: dir }, path, query );
|
||||
case 'compare':
|
||||
return ( key, queryParam, ids ) =>
|
||||
updateQueryString(
|
||||
{
|
||||
[ queryParam ]: `compare-${ key }`,
|
||||
[ key ]: ids,
|
||||
search: undefined,
|
||||
},
|
||||
path,
|
||||
query
|
||||
);
|
||||
default:
|
||||
return ( value ) =>
|
||||
updateQueryString( { [ param ]: value }, path, query );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a URL is a WC admin url.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: dev
|
||||
|
||||
Fix lint issues
|
|
@ -13,7 +13,6 @@ import { Link } from '@woocommerce/components';
|
|||
*/
|
||||
import { PaymentMethodsIcons } from './PaymentMethodsIcons';
|
||||
import { WCPayBannerImage } from './WCPayBannerImage';
|
||||
import { WCPayBannerImageCut } from './WCPayBannerImageCut';
|
||||
|
||||
export const WCPayBannerFooter: React.VFC = () => (
|
||||
<CardFooter className="woocommerce-recommended-payments-banner__footer">
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# Changelog
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: dev
|
||||
|
||||
Fix lint issues
|
|
@ -0,0 +1,5 @@
|
|||
Significance: patch
|
||||
Type: dev
|
||||
Comment: Added a blank changelog file
|
||||
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { isValidElement, Fragment } from 'react';
|
||||
import { Slot, Fill } from '@wordpress/components';
|
||||
import { isValidElement } from 'react';
|
||||
import { Fill } from '@wordpress/components';
|
||||
import { cloneElement, createElement } from '@wordpress/element';
|
||||
|
||||
type ChildrenProps = {
|
||||
|
|
|
@ -6,6 +6,7 @@ module.exports = {
|
|||
files: [ 'client/**/*.js', 'client/**/*.jsx', 'client/**/*.tsx' ],
|
||||
rules: {
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'warn',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
import { render } from '@testing-library/react';
|
||||
import { numberFormat } from '@woocommerce/number';
|
||||
import CurrencyFactory from '@woocommerce/currency';
|
||||
import { CurrencyFactory } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
|
|
@ -23,11 +23,11 @@ import {
|
|||
getChartTypeForQuery,
|
||||
getPreviousDate,
|
||||
} from '@woocommerce/date';
|
||||
import { CurrencyContext } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { CurrencyContext } from '../../../lib/currency-context';
|
||||
import ReportError from '../report-error';
|
||||
import { getChartMode, getSelectedFilter, createDateFormatter } from './utils';
|
||||
|
||||
|
|
|
@ -14,11 +14,11 @@ import {
|
|||
isoDateFormat,
|
||||
} from '@woocommerce/date';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
import { CurrencyContext } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { CurrencyContext } from '../../../lib/currency-context';
|
||||
import { STORE_KEY as CES_STORE_KEY } from '../../../customer-effort-score-tracks/data/constants';
|
||||
import { LOCALE } from '~/utils/admin-settings';
|
||||
|
||||
|
|
|
@ -16,12 +16,12 @@ import { calculateDelta, formatValue } from '@woocommerce/number';
|
|||
import { getSummaryNumbers, SETTINGS_STORE_NAME } from '@woocommerce/data';
|
||||
import { getDateParamsFromQuery } from '@woocommerce/date';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
import { CurrencyContext } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ReportError from '../report-error';
|
||||
import { CurrencyContext } from '../../../lib/currency-context';
|
||||
|
||||
/**
|
||||
* Component to render summary numbers in reports.
|
||||
|
|
|
@ -10,13 +10,13 @@ import { getNewPath, getPersistedQuery } from '@woocommerce/navigation';
|
|||
import { Link } from '@woocommerce/components';
|
||||
import { formatValue } from '@woocommerce/number';
|
||||
import { ITEMS_STORE_NAME } from '@woocommerce/data';
|
||||
import { CurrencyContext } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import CategoryBreacrumbs from './breadcrumbs';
|
||||
import ReportTable from '../../components/report-table';
|
||||
import { CurrencyContext } from '../../../lib/currency-context';
|
||||
|
||||
class CategoriesReportTable extends Component {
|
||||
constructor( props ) {
|
||||
|
|
|
@ -8,12 +8,12 @@ import { Date, Link } from '@woocommerce/components';
|
|||
import { getNewPath, getPersistedQuery } from '@woocommerce/navigation';
|
||||
import { formatValue } from '@woocommerce/number';
|
||||
import { defaultTableDateFormat } from '@woocommerce/date';
|
||||
import { CurrencyContext } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ReportTable from '../../components/report-table';
|
||||
import { CurrencyContext } from '../../../lib/currency-context';
|
||||
import { getAdminSetting } from '~/utils/admin-settings';
|
||||
|
||||
class CouponsReportTable extends Component {
|
||||
|
|
|
@ -10,12 +10,12 @@ import { formatValue } from '@woocommerce/number';
|
|||
import { getAdminLink } from '@woocommerce/settings';
|
||||
import { defaultTableDateFormat } from '@woocommerce/date';
|
||||
import { COUNTRIES_STORE_NAME } from '@woocommerce/data';
|
||||
import { CurrencyContext } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ReportTable from '../../components/report-table';
|
||||
import { CurrencyContext } from '../../../lib/currency-context';
|
||||
import { getAdminSetting } from '~/utils/admin-settings';
|
||||
|
||||
function CustomersReportTable( {
|
||||
|
|
|
@ -12,12 +12,12 @@ import { formatValue } from '@woocommerce/number';
|
|||
import { getAdminLink } from '@woocommerce/settings';
|
||||
import { SETTINGS_STORE_NAME } from '@woocommerce/data';
|
||||
import { getCurrentDates, defaultTableDateFormat } from '@woocommerce/date';
|
||||
import { CurrencyContext } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ReportTable from '../../components/report-table';
|
||||
import { CurrencyContext } from '../../../lib/currency-context';
|
||||
import { getAdminSetting } from '~/utils/admin-settings';
|
||||
|
||||
class DownloadsReportTable extends Component {
|
||||
|
|
|
@ -8,6 +8,10 @@ import PropTypes from 'prop-types';
|
|||
import { find } from 'lodash';
|
||||
import { getQuery, getSearchWords } from '@woocommerce/navigation';
|
||||
import { searchItemsByString, ITEMS_STORE_NAME } from '@woocommerce/data';
|
||||
import {
|
||||
CurrencyContext,
|
||||
getFilteredCurrencyInstance,
|
||||
} from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -15,10 +19,6 @@ import { searchItemsByString, ITEMS_STORE_NAME } from '@woocommerce/data';
|
|||
import './style.scss';
|
||||
import { NoMatch } from '~/layout/NoMatch';
|
||||
import ReportError from '../components/report-error';
|
||||
import {
|
||||
CurrencyContext,
|
||||
getFilteredCurrencyInstance,
|
||||
} from '../../lib/currency-context';
|
||||
import getReports from './get-reports';
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,13 +8,13 @@ import { Date, Link, OrderStatus, ViewMoreList } from '@woocommerce/components';
|
|||
import { formatValue } from '@woocommerce/number';
|
||||
import { getNewPath, getPersistedQuery } from '@woocommerce/navigation';
|
||||
import { defaultTableDateFormat } from '@woocommerce/date';
|
||||
import { CurrencyContext } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ReportTable from '../../components/report-table';
|
||||
import { getAdminSetting } from '~/utils/admin-settings';
|
||||
import { CurrencyContext } from '../../../lib/currency-context';
|
||||
|
||||
const capitalizeFirstLetter = ( expr ) =>
|
||||
expr.charAt( 0 ).toUpperCase() + expr.slice( 1 );
|
||||
|
|
|
@ -12,6 +12,7 @@ import { Link, Tag } from '@woocommerce/components';
|
|||
import { formatValue } from '@woocommerce/number';
|
||||
import { getAdminLink } from '@woocommerce/settings';
|
||||
import { ITEMS_STORE_NAME } from '@woocommerce/data';
|
||||
import { CurrencyContext } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -19,7 +20,6 @@ import { ITEMS_STORE_NAME } from '@woocommerce/data';
|
|||
import CategoryBreacrumbs from '../categories/breadcrumbs';
|
||||
import { isLowStock } from './utils';
|
||||
import ReportTable from '../../components/report-table';
|
||||
import { CurrencyContext } from '../../../lib/currency-context';
|
||||
import { getAdminSetting } from '~/utils/admin-settings';
|
||||
|
||||
import './style.scss';
|
||||
|
|
|
@ -21,13 +21,13 @@ import {
|
|||
getCurrentDates,
|
||||
} from '@woocommerce/date';
|
||||
import { stringify } from 'qs';
|
||||
import { CurrencyContext } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ReportTable from '../../components/report-table';
|
||||
import { getAdminSetting } from '~/utils/admin-settings';
|
||||
import { CurrencyContext } from '../../../lib/currency-context';
|
||||
|
||||
const EMPTY_ARRAY = [];
|
||||
|
||||
|
|
|
@ -8,13 +8,13 @@ import { Link } from '@woocommerce/components';
|
|||
import { getNewPath, getPersistedQuery } from '@woocommerce/navigation';
|
||||
import { formatValue } from '@woocommerce/number';
|
||||
import { getAdminLink } from '@woocommerce/settings';
|
||||
import { CurrencyContext } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ReportTable from '../../components/report-table';
|
||||
import { isLowStock } from './utils';
|
||||
import { CurrencyContext } from '../../../lib/currency-context';
|
||||
import { getAdminSetting } from '~/utils/admin-settings';
|
||||
|
||||
const stockStatuses = getAdminSetting( 'stockStatuses', {} );
|
||||
|
|
|
@ -7,14 +7,13 @@ import { map } from 'lodash';
|
|||
import { Link } from '@woocommerce/components';
|
||||
import { getNewPath, getPersistedQuery } from '@woocommerce/navigation';
|
||||
import { formatValue } from '@woocommerce/number';
|
||||
import { CurrencyContext } from '@woocommerce/currency';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getTaxCode } from './utils';
|
||||
import ReportTable from '../../components/report-table';
|
||||
import { CurrencyContext } from '../../../lib/currency-context';
|
||||
|
||||
class TaxesReportTable extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue