Merge branch 'trunk' into pr/36885

This commit is contained in:
Vedanshu Jain 2023-04-07 18:07:24 +05:30
commit e85e68e0c5
882 changed files with 22391 additions and 10884 deletions

View File

@ -39,7 +39,8 @@ If you have questions about the process to contribute code or want to discuss de
- Ensure that your code supports the minimum supported versions of PHP and WordPress; this is shown at the top of the `readme.txt` file.
- Push the changes to your fork and submit a pull request on the trunk branch of the WooCommerce repository.
- Make sure to write good and detailed commit messages (see [this post](https://chris.beams.io/posts/git-commit/) for more on this) and follow all the applicable sections of the pull request template.
- Please avoid modifying the changelog directly or updating the .pot files. These will be updated by the WooCommerce team.
- Please create a change file for your changes by running `pnpm --filter=<project> changelog add`. For example, a change file for the WooCommerce Core project would be added by running `pnpm --filter=woocommerce changelog add`.
- Please avoid modifying the changelog directly or updating the .pot files. These will be updated by the WooCommerce team.
If you are contributing code to the (Javascript-driven) Gutenberg blocks, note that it's developed in an external package.

View File

@ -1,10 +1,9 @@
### All Submissions:
### Submission Review Guidelines:
- [ ] Have you followed the [WooCommerce Contributing guideline](https://github.com/woocommerce/woocommerce/blob/trunk/.github/CONTRIBUTING.md)?
- [ ] Does your code follow the [WordPress' coding standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards/)?
- [ ] Have you checked to ensure there aren't other open [Pull Requests](https://github.com/woocommerce/woocommerce/pulls) for the same update/change?
<!-- Mark completed items with an [x] -->
- I have followed the [WooCommerce Contributing Guidelines](https://github.com/woocommerce/woocommerce/blob/trunk/.github/CONTRIBUTING.md) and the [WordPress Coding Standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards/).
- I have checked to ensure there aren't other open [Pull Requests](https://github.com/woocommerce/woocommerce/pulls) for the same update/change.
- I have reviewed my code for [security best practices](https://developer.wordpress.org/apis/security/).
- Following the above guidelines will result in quick merges and clear and detailed feedback when appropriate.
<!-- You can erase any parts of this template not applicable to your Pull Request. -->
@ -18,25 +17,12 @@ Closes # .
### How to test the changes in this Pull Request:
<!-- Please include detailed instructions on how these changes can be tested, make sure to review and follow the guide for writing high-quality testing instructions below. -->
<!-- Include detailed instructions on how these changes can be tested. Review and follow the guide for how to write high-quality testing instructions. -->
- [ ] Have you followed the [Writing high-quality testing instructions guide](https://github.com/woocommerce/woocommerce/wiki/Writing-high-quality-testing-instructions)?
Using the [WooCommerce Testing Instructions Guide](https://github.com/woocommerce/woocommerce/wiki/Writing-high-quality-testing-instructions), include your detailed testing instructions:
1.
2.
3.
<!-- End testing instructions -->
### Other information:
- [ ] Have you added an explanation of what your changes do and why you'd like us to include them?
- [ ] Have you written new tests for your changes, as applicable?
- [ ] Have you created a changelog file for each project being changed, ie `pnpm --filter=<project> changelog add`?
- [ ] Have you included testing instructions?
<!-- Mark completed items with an [x] -->
### FOR PR REVIEWER ONLY:
- [ ] I have reviewed that everything is sanitized/escaped appropriately for any SQL or XSS injection possibilities. I made sure Linting is not ignored or disabled.
<!-- End testing instructions -->

View File

@ -29,7 +29,7 @@ runs:
- name: Setup PNPM
uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd
with:
version: '^7.22.0'
version: '7.29.1'
- name: Setup Node
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c

View File

@ -0,0 +1,38 @@
name: Run API tests
description: Runs the WooCommerce Core API tests and generates Allure report.
permissions: {}
inputs:
report-name:
description: Name of Allure report to be generated.
required: true
tests:
description: Specific tests to run, separated by single whitespace. See https://playwright.dev/docs/test-cli
runs:
using: composite
steps:
- name: Run API tests.
id: run-api-tests
working-directory: plugins/woocommerce
shell: bash
run: |
pnpm exec playwright test \
--config=tests/api-core-tests/playwright.config.js \
${{ inputs.tests }}
- name: Generate Test report.
if: success() || ( failure() && steps.run-api-tests.conclusion == 'failure' )
working-directory: plugins/woocommerce
shell: bash
run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }}
- name: Archive test report
if: success() || ( failure() && steps.run-api-tests.conclusion == 'failure' )
uses: actions/upload-artifact@v3
with:
name: ${{ inputs.report-name }}
path: |
${{ env.ALLURE_RESULTS_DIR }}
${{ env.ALLURE_REPORT_DIR }}
retention-days: 20

View File

@ -0,0 +1,49 @@
name: Run E2E tests
description: Runs the WooCommerce Core E2E tests and generates Allure report.
permissions: {}
inputs:
report-name:
description: Name of Allure report to be generated.
required: true
tests:
description: Specific tests to run, separated by single whitespace. See https://playwright.dev/docs/test-cli
playwright-config:
description: The Playwright configuration file to use.
default: playwright.config.js
runs:
using: composite
steps:
- name: Download and install Chromium browser.
working-directory: plugins/woocommerce
shell: bash
run: pnpm exec playwright install chromium
- name: Run E2E tests.
id: run-e2e-tests
env:
FORCE_COLOR: 1
USE_WP_ENV: 1
working-directory: plugins/woocommerce
shell: bash
run: |
pnpm exec playwright test \
--config=tests/e2e-pw/${{ inputs.playwright-config }} \
${{ inputs.tests }}
- name: Generate Test report.
if: success() || ( failure() && steps.run-e2e-tests.conclusion == 'failure' )
working-directory: plugins/woocommerce
shell: bash
run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }}
- name: Archive test report
if: success() || ( failure() && steps.run-e2e-tests.conclusion == 'failure' )
uses: actions/upload-artifact@v3
with:
name: ${{ inputs.report-name }}
path: |
${{ env.ALLURE_RESULTS_DIR }}
${{ env.ALLURE_REPORT_DIR }}
retention-days: 20

View File

@ -0,0 +1,17 @@
name: Run k6 performance tests
description: Runs the WooCommerce Core k6 performance tests.
permissions: {}
runs:
using: composite
steps:
- name: Install k6
shell: bash
run: |
curl https://github.com/grafana/k6/releases/download/v0.33.0/k6-v0.33.0-linux-amd64.tar.gz -L | tar xvz --strip-components 1
- name: Run k6 performance tests
id: run-k6-tests
shell: bash
run: |
./k6 run plugins/woocommerce/tests/performance/tests/gh-action-pr-requests.js

View File

@ -0,0 +1,29 @@
name: Setup local test environment
description: Set up a wp-env testing environment
permissions: {}
inputs:
test-type:
required: true
type: choice
options:
- e2e
- api
- k6
runs:
using: composite
steps:
- name: Load docker images and start containers for E2E or API tests
if: ( inputs.test-type == 'e2e' ) || ( inputs.test-type == 'api' )
working-directory: plugins/woocommerce
shell: bash
run: pnpm run env:test
- name: Load docker images and start containers for k6 performance tests
if: inputs.test-type == 'k6'
working-directory: plugins/woocommerce
shell: bash
run: |
pnpm env:dev --filter=woocommerce
pnpm env:performance-init --filter=woocommerce

View File

@ -0,0 +1,41 @@
name: Send Slack alert on PR merge test failure
description: Send a Slack alert when automated tests failed on trunk after PR merge.
permissions: {}
inputs:
slack-bot-token:
required: true
channel-id:
required: true
test-type:
required: true
type: choice
options:
- E2E
- API
- k6
runs:
using: composite
steps:
- name: Compose Slack message
id: compose-slack-message
uses: actions/github-script@v6
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
SHA: ${{ github.event.pull_request.merge_commit_sha }}
TEST_TYPE: ${{ inputs.test-type }}
with:
script: |
const script = require('./.github/actions/tests/slack-alert-on-pr-merge/scripts/compose-slack-message.js')
const slackMessage = script()
core.setOutput('slack-message', slackMessage)
- name: Send Slack alert
uses: slackapi/slack-github-action@v1.23.0
env:
SLACK_BOT_TOKEN: ${{ inputs.slack-bot-token }}
with:
channel-id: ${{ inputs.channel-id }}
payload: ${{ steps.compose-slack-message.outputs.slack-message }}

View File

@ -0,0 +1,114 @@
module.exports = () => {
const {
GITHUB_BASE_REF,
GITHUB_RUN_ID,
PR_NUMBER,
PR_TITLE,
SHA,
TEST_TYPE,
} = process.env;
// Slack message blocks
const blocks = [];
const dividerBlock = {
type: 'divider',
};
const introBlock = {
type: 'section',
text: {
type: 'mrkdwn',
text: `${ TEST_TYPE } tests failed on \`${ GITHUB_BASE_REF }\` after merging PR <https://github.com/woocommerce/woocommerce/pull/${ PR_NUMBER }|#${ PR_NUMBER }>`,
},
};
const prTitleBlock = {
type: 'header',
text: {
type: 'plain_text',
text: PR_TITLE,
emoji: true,
},
};
const prButtonBlock = {
type: 'actions',
elements: [
{
type: 'button',
text: {
type: 'plain_text',
text: 'View pull request :pr-merged:',
emoji: true,
},
value: 'view_pr',
url: `https://github.com/woocommerce/woocommerce/pull/${ PR_NUMBER }`,
action_id: 'view-pr',
},
],
};
const mergeCommitBlock = {
type: 'actions',
elements: [
{
type: 'button',
text: {
type: 'plain_text',
text: `View merge commit ${ SHA.substring(
0,
7
) } :alphabet-yellow-hash:`,
emoji: true,
},
value: 'view_commit',
url: `https://github.com/woocommerce/woocommerce/commit/${ SHA }`,
action_id: 'view-commit',
},
],
};
const githubBlock = {
type: 'actions',
elements: [
{
type: 'button',
text: {
type: 'plain_text',
text: 'View GitHub run log :github:',
emoji: true,
},
value: 'view_github',
url: `https://github.com/woocommerce/woocommerce/actions/runs/${ GITHUB_RUN_ID }`,
action_id: 'view-github',
},
],
};
const reportBlock = {
type: 'actions',
elements: [
{
type: 'button',
text: {
type: 'plain_text',
text: 'View test report :colorful-bar-chart:',
emoji: true,
},
value: 'view_report',
url: `https://woocommerce.github.io/woocommerce-test-reports/pr-merge/${ PR_NUMBER }/${ TEST_TYPE.toLowerCase() }`,
action_id: 'view-report',
},
],
};
// Assemble blocks
blocks.push( dividerBlock );
blocks.push( introBlock );
blocks.push( prTitleBlock );
blocks.push( prButtonBlock );
blocks.push( mergeCommitBlock );
blocks.push( githubBlock );
if ( [ 'e2e', 'api' ].includes( TEST_TYPE.toLowerCase() ) ) {
blocks.push( reportBlock );
}
blocks.push( dividerBlock );
return { blocks };
};

View File

@ -0,0 +1,37 @@
name: Upload Allure files to bucket
description: Upload Allure files to bucket.
permissions: {}
inputs:
artifact-name:
description: Name of the artifact that contains the allure-report and/or allure-results folders.
required: true
aws-region:
required: true
aws-access-key-id:
required: true
aws-secret-access-key:
required: true
s3-bucket:
required: true
include-allure-results:
dafault: false
runs:
using: composite
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-region: ${{ inputs.aws-region }}
aws-access-key-id: ${{ inputs.aws-access-key-id }}
aws-secret-access-key: ${{ inputs.aws-secret-access-key }}
- name: Upload Allure artifact
env:
ARTIFACT_NAME: ${{ inputs.artifact-name }}
S3_BUCKET: ${{ inputs.s3-bucket }}
INCLUDE_ALLURE_RESULTS: ${{ inputs.include-allure-results }}
shell: bash
working-directory: .github/actions/tests/upload-allure-files-to-bucket/scripts
run: bash upload-allure-artifact.sh

View File

@ -0,0 +1,30 @@
#!/usr/bin/env bash
s3_upload () {
aws s3 cp "$1" "$2" \
--recursive
}
upload_allure_results () {
if [[ $INCLUDE_ALLURE_RESULTS != "true" ]]; then
return
fi
SOURCE="$ALLURE_RESULTS_DIR"
DESTINATION="$S3_BUCKET/artifacts/$GITHUB_RUN_ID/$ARTIFACT_NAME/allure-results"
s3_upload "$SOURCE" "$DESTINATION"
}
upload_allure_report () {
SOURCE="$ALLURE_REPORT_DIR"
DESTINATION="$S3_BUCKET/artifacts/$GITHUB_RUN_ID/$ARTIFACT_NAME/allure-report"
s3_upload "$SOURCE" "$DESTINATION"
}
upload_allure_results
upload_allure_report
EXIT_CODE=$(echo $?)
exit $EXIT_CODE

View File

@ -65,3 +65,12 @@
- plugins/woocommerce/src/Admin/**/*
- plugins/woocommerce/src/Internal/Admin/**/*
- plugins/woocommerce-admin/**/*
'focus: performance tests [team:Solaris]':
- plugins/woocommerce/tests/performance/**/*
'focus: api tests [team:Solaris]':
- plugins/woocommerce/tests/api-core-tests/**/*
'focus: e2e tests [team:Solaris]':
- plugins/woocommerce/tests/e2e-pw/**/*

View File

@ -31,9 +31,9 @@ jobs:
include:
- wp: nightly
php: '7.4'
- wp: '5.9'
- wp: '6.0'
php: 7.4
- wp: '5.8'
- wp: '5.9'
php: 7.4
services:
database:

View File

@ -173,7 +173,7 @@ jobs:
return await script( { core } )
- name: Find PR comment by github-actions[bot]
uses: peter-evans/find-comment@v2
uses: peter-evans/find-comment@034abe94d3191f9c89d870519735beae326f2bdb
id: find-comment
with:
issue-number: ${{ github.event.pull_request.number }}
@ -181,7 +181,7 @@ jobs:
body-includes: Test Results Summary
- name: Create or update PR comment
uses: peter-evans/create-or-update-comment@v2
uses: peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}

View File

@ -219,7 +219,7 @@ jobs:
return await script( { core } )
- name: Find PR comment by github-actions[bot]
uses: peter-evans/find-comment@v2
uses: peter-evans/find-comment@034abe94d3191f9c89d870519735beae326f2bdb
id: find-comment
with:
issue-number: ${{ github.event.pull_request.number }}
@ -227,7 +227,7 @@ jobs:
body-includes: Test Results Summary
- name: Create or update PR comment
uses: peter-evans/create-or-update-comment@v2
uses: peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}

View File

@ -20,7 +20,7 @@ jobs:
uses: actions/checkout@v3
- name: Install prerequisites
run: |
npm install -g pnpm
npm install -g pnpm@7
npm -g i @wordpress/env@5.1.0
pnpm install --filter code-analyzer --filter cli-core
- name: Run analyzer

View File

@ -15,12 +15,14 @@ permissions: {}
jobs:
test:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }}
name: PHP ${{ matrix.php }} WP ${{ matrix.wp }}
name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} ${{ matrix.hpos && 'HPOS' || '' }}
timeout-minutes: 30
runs-on: ubuntu-20.04
permissions:
contents: read
continue-on-error: ${{ matrix.wp == 'nightly' }}
env:
HPOS: ${{ matrix.hpos }}
strategy:
fail-fast: false
matrix:
@ -33,6 +35,9 @@ jobs:
php: 7.4
- wp: '5.9'
php: 7.4
- wp: 'latest'
php: '7.4'
hpos: true
services:
database:
image: mysql:5.6

View File

@ -0,0 +1,48 @@
name: Remind reviewers to also review the testing instructions.
on:
pull_request:
types: [review_requested]
permissions: {}
jobs:
add-testing-instructions-review-comment:
runs-on: ubuntu-20.04
permissions:
pull-requests: write
steps:
- name: Get the username of requested reviewers
id: get_reviewer_username
run: |
# Retrieves the username of all reviewers and stores them in a comma-separated list
reviewers=$(echo '${{ toJson(github.event.pull_request.requested_reviewers[*].login) }}' | jq -r 'map("@\(.)") | join(", ")')
echo "REVIEWERS=$reviewers" >> $GITHUB_ENV
- name: Get the name of requested teams
id: get_team_name
run: |
# Retrieves the name of all teams asked for review and stores them in a comma-separated list
teams=$(echo '${{ toJson(github.event.pull_request.requested_teams[*].slug) }}' | jq -r 'map("@woocommerce/\(.)") | join(", ")')
echo "TEAMS=$teams" >> $GITHUB_ENV
- name: Find the comment by github-actions[bot] asking for reviewing the testing instructions
uses: peter-evans/find-comment@034abe94d3191f9c89d870519735beae326f2bdb
id: find-comment
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: please make sure to review the testing instructions
- name: Create or update PR comment asking for reviewers to review the testing instructions
uses: peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
Hi ${{ env.REVIEWERS }}, ${{ env.TEAMS }}
Apart from reviewing the code changes, please make sure to review the testing instructions as well.
You can follow this guide to find out what good testing instructions should look like:
https://github.com/woocommerce/woocommerce/wiki/Writing-high-quality-testing-instructions
edit-mode: replace

View File

@ -0,0 +1,168 @@
name: Run tests against trunk after PR merge
on:
pull_request:
types:
- closed
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
permissions: {}
jobs:
api:
name: Run API tests
runs-on: ubuntu-20.04
if: (github.event.pull_request.merged == true) && (github.event.pull_request.base.ref == 'trunk')
permissions:
contents: read
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
ARTIFACT_NAME: api-pr-merge-${{ github.event.pull_request.number }}-run-${{ github.run_number }}
steps:
- name: Checkout merge commit on trunk
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
- name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo
with:
build-filters: woocommerce
- name: Setup local test environment
uses: ./.github/actions/tests/setup-local-test-environment
with:
test-type: api
- name: Run API tests
id: run-api-composite-action
uses: ./.github/actions/tests/run-api-tests
with:
report-name: ${{ env.ARTIFACT_NAME }}
- name: Upload Allure files to bucket
if: success() || ( failure() && steps.run-api-composite-action.conclusion == 'failure' )
uses: ./.github/actions/tests/upload-allure-files-to-bucket
with:
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-secret-access-key: ${{ secrets.REPORTS_AWS_SECRET_ACCESS_KEY }}
artifact-name: ${{ env.ARTIFACT_NAME }}
s3-bucket: ${{ secrets.REPORTS_BUCKET }}
- name: Publish Allure report
if: success() || ( failure() && steps.run-api-composite-action.conclusion == 'failure' )
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
run: |
gh workflow run publish-test-reports-trunk-merge.yml \
-f run_id=${{ github.run_id }} \
-f artifact=${{ env.ARTIFACT_NAME }} \
-f pr_number=${{ github.event.pull_request.number }} \
-f test_type="api" \
--repo woocommerce/woocommerce-test-reports
- name: Send Slack alert on test failure
if: failure() && steps.run-api-composite-action.conclusion == 'failure'
uses: ./.github/actions/tests/slack-alert-on-pr-merge
with:
slack-bot-token: ${{ secrets.E2E_SLACK_TOKEN }}
channel-id: ${{ secrets.E2E_TRUNK_SLACK_CHANNEL }}
test-type: API
e2e:
name: Run E2E tests
needs: [api]
runs-on: ubuntu-20.04
permissions:
contents: read
env:
ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-results
ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-report
ARTIFACT_NAME: e2e-pr-merge-${{ github.event.pull_request.number }}-run-${{ github.run_number }}
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
- name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo
with:
build-filters: woocommerce
- name: Setup local test environment
uses: ./.github/actions/tests/setup-local-test-environment
with:
test-type: e2e
- name: Run E2E tests
id: run-e2e-composite-action
timeout-minutes: 60
uses: ./.github/actions/tests/run-e2e-tests
env:
E2E_MAX_FAILURES: 15
with:
report-name: ${{ env.ARTIFACT_NAME }}
- name: Upload Allure files to bucket
if: success() || ( failure() && steps.run-e2e-composite-action.conclusion == 'failure' )
uses: ./.github/actions/tests/upload-allure-files-to-bucket
with:
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-secret-access-key: ${{ secrets.REPORTS_AWS_SECRET_ACCESS_KEY }}
artifact-name: ${{ env.ARTIFACT_NAME }}
s3-bucket: ${{ secrets.REPORTS_BUCKET }}
include-allure-results: false
- name: Publish Allure report
if: success() || ( failure() && steps.run-e2e-composite-action.conclusion == 'failure' )
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
run: |
gh workflow run publish-test-reports-trunk-merge.yml \
-f run_id=${{ github.run_id }} \
-f artifact=${{ env.ARTIFACT_NAME }} \
-f pr_number=${{ github.event.pull_request.number }} \
-f test_type="e2e" \
--repo woocommerce/woocommerce-test-reports
- name: Send Slack alert on test failure
if: failure() && steps.run-e2e-composite-action.conclusion == 'failure'
uses: ./.github/actions/tests/slack-alert-on-pr-merge
with:
slack-bot-token: ${{ secrets.E2E_SLACK_TOKEN }}
channel-id: ${{ secrets.E2E_TRUNK_SLACK_CHANNEL }}
test-type: E2E
k6:
name: Run k6 Performance tests
needs: [api]
runs-on: ubuntu-20.04
permissions:
contents: read
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
- name: Setup WooCommerce Monorepo
uses: ./.github/actions/setup-woocommerce-monorepo
- name: Setup local test environment
uses: ./.github/actions/tests/setup-local-test-environment
with:
test-type: k6
- name: Run k6 performance tests
id: run-k6-composite-action
uses: './.github/actions/tests/run-k6-tests'
- name: Send Slack alert on test failure
if: failure() && steps.run-k6-composite-action.conclusion == 'failure'
uses: ./.github/actions/tests/slack-alert-on-pr-merge
with:
slack-bot-token: ${{ secrets.E2E_SLACK_TOKEN }}
channel-id: ${{ secrets.E2E_TRUNK_SLACK_CHANNEL }}
test-type: k6

View File

@ -12,9 +12,8 @@ concurrency:
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
E2E_WP_LATEST_ARTIFACT: E2E test on release smoke test site with WP Latest (run ${{ github.run_number }})
E2E_UPDATE_WC_ARTIFACT: WooCommerce version update test on release smoke test site (run ${{ github.run_number }})
jobs:
get-tag:
@ -80,12 +79,13 @@ 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
- name: Run E2E tests
id: run-e2e-composite-action
timeout-minutes: 60
uses: ./.github/actions/tests/run-e2e-tests
with:
report-name: ${{ env.E2E_UPDATE_WC_ARTIFACT }}
tests: update-woocommerce.spec.js
env:
ADMIN_PASSWORD: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }}
ADMIN_USER: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
@ -95,36 +95,19 @@ jobs:
DEFAULT_TIMEOUT_OVERRIDE: 120000
GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
UPDATE_WC: ${{ needs.get-tag.outputs.tag }}
run: |
pnpm exec playwright test \
--config=tests/e2e-pw/playwright.config.js \
update-woocommerce.spec.js
- name: Generate 'Update WooCommerce' test report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }}
- name: Configure AWS credentials
if: success() || failure()
uses: aws-actions/configure-aws-credentials@v1-node16
- name: Upload Allure artifacts to bucket
if: success() || ( failure() && steps.run-e2e-composite-action.conclusion == 'failure' )
uses: ./.github/actions/tests/upload-allure-files-to-bucket
with:
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
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
artifact-name: ${{ env.E2E_WP_LATEST_ARTIFACT }}
s3-bucket: ${{ secrets.REPORTS_BUCKET }}
- name: Publish E2E Allure report
if: success() || failure()
if: success() || ( failure() && steps.run-e2e-composite-action.conclusion == 'failure' )
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
ENV_DESCRIPTION: wp-latest
@ -139,17 +122,6 @@ jobs:
-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
@ -159,7 +131,7 @@ jobs:
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 }}
API_WP_LATEST_ARTIFACT: API test on release smoke test site with WP Latest (run ${{ github.run_number }})
steps:
- uses: actions/checkout@v3
@ -169,41 +141,29 @@ jobs:
install-filters: woocommerce
build: false
- name: Run API tests.
working-directory: plugins/woocommerce
- name: Run API tests
id: run-api-composite-action
uses: ./.github/actions/tests/run-api-tests
with:
report-name: ${{ env.API_WP_LATEST_ARTIFACT }}
tests: hello
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-node16
- name: Upload Allure artifacts to bucket
if: success() || ( failure() && steps.run-api-composite-action.conclusion == 'failure' )
uses: ./.github/actions/tests/upload-allure-files-to-bucket
with:
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-secret-access-key: ${{ secrets.REPORTS_AWS_SECRET_ACCESS_KEY }}
- name: Upload Allure files to bucket
if: success() || failure()
run: |
aws s3 cp ${{ env.ALLURE_RESULTS_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.API_WP_LATEST_ARTIFACT }}/allure-results \
--recursive \
--quiet
aws s3 cp ${{ env.ALLURE_REPORT_DIR }} \
${{ secrets.REPORTS_BUCKET }}/artifacts/${{ github.run_id }}/${{ env.API_WP_LATEST_ARTIFACT }}/allure-report \
--recursive \
--quiet
artifact-name: ${{ env.API_WP_LATEST_ARTIFACT }}
s3-bucket: ${{ secrets.REPORTS_BUCKET }}
- name: Publish API Allure report
if: success() || failure()
if: success() || ( failure() && steps.run-api-composite-action.conclusion == 'failure' )
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
ENV_DESCRIPTION: wp-latest
@ -218,17 +178,6 @@ jobs:
-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
@ -247,11 +196,13 @@ jobs:
install-filters: woocommerce
build: false
- name: Download and install Chromium browser.
working-directory: plugins/woocommerce
run: pnpm exec playwright install chromium
- name: Run E2E tests
id: run-e2e-composite-action
timeout-minutes: 60
uses: ./.github/actions/tests/run-e2e-tests
with:
report-name: e2e-wp-latest--partial--run-${{ github.run_number }}
playwright-config: ignore-plugin-tests.playwright.config.js
env:
ADMIN_PASSWORD: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }}
ADMIN_USER: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
@ -262,9 +213,6 @@ jobs:
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()
@ -283,23 +231,26 @@ jobs:
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }}
- name: Configure AWS credentials
- name: Archive E2E test report
if: success() || failure()
uses: aws-actions/configure-aws-credentials@v1-node16
uses: actions/upload-artifact@v3
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: ${{ env.E2E_WP_LATEST_ARTIFACT }}
path: |
${{ env.ALLURE_RESULTS_DIR }}
${{ env.ALLURE_REPORT_DIR }}
if-no-files-found: ignore
retention-days: 5
- name: Upload report 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: Upload Allure artifacts to bucket
if: success() || ( failure() && steps.run-e2e-composite-action.conclusion == 'failure' )
uses: ./.github/actions/tests/upload-allure-files-to-bucket
with:
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-secret-access-key: ${{ secrets.REPORTS_AWS_SECRET_ACCESS_KEY }}
artifact-name: ${{ env.E2E_WP_LATEST_ARTIFACT }}
s3-bucket: ${{ secrets.REPORTS_BUCKET }}
- name: Publish E2E Allure report
if: success() || failure()
@ -317,17 +268,6 @@ jobs:
-f test_type="e2e" \
--repo woocommerce/woocommerce-test-reports
- 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
get-wp-versions:
name: Get WP L-1 & L-2 version numbers
needs: [get-tag]
@ -371,14 +311,15 @@ jobs:
runs-on: ubuntu-20.04
needs: [get-wp-versions]
strategy:
fail-fast: false
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 }}
API_WP_LATEST_X_ARTIFACT: API test on wp-env with WordPress ${{ matrix.version.number }} (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 }}
E2E_WP_LATEST_X_ARTIFACT: E2E test on wp-env with WordPress ${{ matrix.version.number }} (run ${{ github.run_number }})
permissions:
contents: read
steps:
@ -414,38 +355,31 @@ jobs:
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
- name: Run API tests
id: run-api-composite-action
uses: ./.github/actions/tests/run-api-tests
with:
report-name: ${{ env.API_WP_LATEST_X_ARTIFACT }}
tests: hello
env:
ALLURE_RESULTS_DIR: ${{ env.API_ALLURE_RESULTS_DIR }}
run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello
ALLURE_REPORT_DIR: ${{ env.API_ALLURE_REPORT_DIR }}
- name: Generate API Allure report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.API_ALLURE_RESULTS_DIR }} --output ${{ env.API_ALLURE_REPORT_DIR }}
- name: Configure AWS credentials
if: success() || failure()
uses: aws-actions/configure-aws-credentials@v1-node16
- name: Upload Allure artifacts to bucket
if: success() || ( failure() && steps.run-api-composite-action.conclusion == 'failure' )
uses: ./.github/actions/tests/upload-allure-files-to-bucket
env:
ALLURE_RESULTS_DIR: ${{ env.API_ALLURE_RESULTS_DIR }}
ALLURE_REPORT_DIR: ${{ env.API_ALLURE_REPORT_DIR }}
with:
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
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
artifact-name: ${{ env.API_WP_LATEST_X_ARTIFACT }}
s3-bucket: ${{ secrets.REPORTS_BUCKET }}
- name: Publish API Allure report
if: success() || failure()
if: success() || ( failure() && steps.run-api-composite-action.conclusion == 'failure' )
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
ENV_DESCRIPTION: ${{ matrix.version.env_description }}
@ -460,65 +394,33 @@ jobs:
-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' )
- name: Run E2E tests
id: run-e2e-composite-action
timeout-minutes: 60
id: e2e
uses: ./.github/actions/tests/run-e2e-tests
env:
USE_WP_ENV: 1
E2E_MAX_FAILURES: 15
FORCE_COLOR: 1
ALLURE_RESULTS_DIR: ${{ env.E2E_ALLURE_RESULTS_DIR }}
ALLURE_REPORT_DIR: ${{ env.E2E_ALLURE_REPORT_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
report-name: ${{ env.E2E_WP_LATEST_X_ARTIFACT }}
- name: Upload Allure artifacts to bucket
if: success() || ( failure() && steps.run-e2e-composite-action.conclusion == 'failure' )
uses: ./.github/actions/tests/upload-allure-files-to-bucket
env:
ALLURE_RESULTS_DIR: ${{ env.E2E_ALLURE_RESULTS_DIR }}
ALLURE_REPORT_DIR: ${{ env.E2E_ALLURE_REPORT_DIR }}
with:
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-secret-access-key: ${{ secrets.REPORTS_AWS_SECRET_ACCESS_KEY }}
artifact-name: ${{ env.E2E_WP_LATEST_X_ARTIFACT }}
s3-bucket: ${{ secrets.REPORTS_BUCKET }}
- name: Publish E2E Allure report
if: success() || failure()
if: success() || ( failure() && steps.run-e2e-composite-action.conclusion == 'failure' )
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
ENV_DESCRIPTION: ${{ matrix.version.env_description }}
@ -538,15 +440,16 @@ jobs:
runs-on: ubuntu-20.04
needs: [get-tag]
strategy:
fail-fast: false
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 }}
API_ARTIFACT: API test on wp-env with 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 }}
E2E_ARTIFACT: E2E test on wp-env with PHP ${{ matrix.php_version }} (run ${{ github.run_number }})
steps:
- name: Checkout
uses: actions/checkout@v3
@ -574,38 +477,31 @@ jobs:
- 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
- name: Run API tests
id: run-api-composite-action
uses: ./.github/actions/tests/run-api-tests
with:
report-name: ${{ env.API_ARTIFACT }}
tests: hello
env:
ALLURE_RESULTS_DIR: ${{ env.API_ALLURE_RESULTS_DIR }}
run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello
ALLURE_REPORT_DIR: ${{ env.API_ALLURE_REPORT_DIR }}
- name: Generate API Allure report.
if: success() || failure()
working-directory: plugins/woocommerce
run: pnpm exec allure generate --clean ${{ env.API_ALLURE_RESULTS_DIR }} --output ${{ env.API_ALLURE_REPORT_DIR }}
- name: Configure AWS credentials
if: success() || failure()
uses: aws-actions/configure-aws-credentials@v1-node16
- name: Upload Allure artifacts to bucket
if: success() || ( failure() && steps.run-api-composite-action.conclusion == 'failure' )
uses: ./.github/actions/tests/upload-allure-files-to-bucket
env:
ALLURE_RESULTS_DIR: ${{ env.API_ALLURE_RESULTS_DIR }}
ALLURE_REPORT_DIR: ${{ env.API_ALLURE_REPORT_DIR }}
with:
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
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
artifact-name: ${{ env.API_ARTIFACT }}
s3-bucket: ${{ secrets.REPORTS_BUCKET }}
- name: Publish API Allure report
if: success() || failure()
if: success() || ( failure() && steps.run-api-composite-action.conclusion == 'failure' )
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
ENV_DESCRIPTION: php-${{ matrix.php_version }}
@ -620,63 +516,33 @@ jobs:
-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' )
- name: Run E2E tests
id: run-e2e-composite-action
timeout-minutes: 60
uses: ./.github/actions/tests/run-e2e-tests
env:
USE_WP_ENV: 1
E2E_MAX_FAILURES: 15
FORCE_COLOR: 1
ALLURE_RESULTS_DIR: ${{ env.E2E_ALLURE_RESULTS_DIR }}
ALLURE_REPORT_DIR: ${{ env.E2E_ALLURE_REPORT_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
E2E_MAX_FAILURES: 15
with:
name: ${{ env.E2E_ARTIFACT }}
path: |
${{ env.E2E_ALLURE_RESULTS_DIR }}
${{ env.E2E_ALLURE_REPORT_DIR }}
if-no-files-found: ignore
retention-days: 5
report-name: ${{ env.E2E_ARTIFACT }}
- name: Upload Allure artifacts to bucket
if: success() || ( failure() && steps.run-e2e-composite-action.conclusion == 'failure' )
uses: ./.github/actions/tests/upload-allure-files-to-bucket
env:
ALLURE_RESULTS_DIR: ${{ env.E2E_ALLURE_RESULTS_DIR }}
ALLURE_REPORT_DIR: ${{ env.E2E_ALLURE_REPORT_DIR }}
with:
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-secret-access-key: ${{ secrets.REPORTS_AWS_SECRET_ACCESS_KEY }}
artifact-name: ${{ env.E2E_ARTIFACT }}
s3-bucket: ${{ secrets.REPORTS_BUCKET }}
- name: Publish E2E Allure report
if: success() || failure()
if: success() || ( failure() && steps.run-e2e-composite-action.conclusion == 'failure' )
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
ENV_DESCRIPTION: php-${{ matrix.php_version }}
@ -690,3 +556,103 @@ jobs:
-f env_description="${{ env.ENV_DESCRIPTION }}" \
-f test_type="e2e" \
--repo woocommerce/woocommerce-test-reports
test-plugins:
name: With ${{ matrix.plugin }}
runs-on: ubuntu-20.04
needs: [get-tag]
env:
ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/allure-results
ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/allure-report
ARTIFACT_NAME: E2E test on wp-env with ${{ matrix.plugin }} installed (run ${{ github.run_number }})
strategy:
fail-fast: false
matrix:
include:
- plugin: 'WooCommerce Payments'
repo: 'automattic/woocommerce-payments'
env_description: 'woocommerce-payments'
- plugin: 'WooCommerce PayPal Payments'
repo: 'woocommerce/woocommerce-paypal-payments'
env_description: 'woocommerce-paypal-payments'
- plugin: 'WooCommerce Shipping & Tax'
repo: 'automattic/woocommerce-services'
env_description: 'woocommerce-shipping-&-tax'
- plugin: 'WooCommerce Subscriptions'
repo: WC_SUBSCRIPTIONS_REPO
private: true
env_description: 'woocommerce-subscriptions'
- plugin: 'WordPress SEO' # Yoast SEO in the UI, but the slug is wordpress-seo
repo: 'Yoast/wordpress-seo'
env_description: 'wordpress-seo'
- plugin: 'Contact Form 7'
repo: 'takayukister/contact-form-7'
env_description: 'contact-form-7'
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
run: pnpm run env:test
- name: Download release zip
env:
GH_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
run: gh release download ${{ needs.get-tag.outputs.tag }} --dir tmp
- name: Replace `plugins/woocommerce` with unzipped woocommerce release build
run: unzip -d plugins -o tmp/woocommerce.zip
- name: Run 'Upload plugin' test
id: run-upload-test
timeout-minutes: 60
uses: ./.github/actions/tests/run-e2e-tests
with:
report-name: ${{ env.ARTIFACT_NAME }}
tests: upload-plugin.spec.js
env:
GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
PLUGIN_NAME: ${{ matrix.plugin }}
PLUGIN_REPOSITORY: ${{ matrix.private && secrets[matrix.repo] || matrix.repo }}
- name: Run the rest of E2E tests
id: run-e2e-composite-action
timeout-minutes: 60
uses: ./.github/actions/tests/run-e2e-tests
with:
playwright-config: ignore-plugin-tests.playwright.config.js
report-name: ${{ env.ARTIFACT_NAME }}
env:
E2E_MAX_FAILURES: 15
- name: Upload Allure artifacts to bucket
if: |
success() ||
( failure() &&
( steps.run-upload-test.conclusion == 'failure' || steps.run-e2e-composite-action.conclusion == 'failure' ) )
uses: ./.github/actions/tests/upload-allure-files-to-bucket
with:
aws-access-key-id: ${{ secrets.REPORTS_AWS_ACCESS_KEY_ID }}
aws-region: ${{ secrets.REPORTS_AWS_REGION }}
aws-secret-access-key: ${{ secrets.REPORTS_AWS_SECRET_ACCESS_KEY }}
artifact-name: ${{ env.ARTIFACT_NAME }}
s3-bucket: ${{ secrets.REPORTS_BUCKET }}
- name: Publish E2E Allure report
if: success() || ( failure() && steps.run-e2e-composite-action.conclusion == 'failure' )
env:
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
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.ARTIFACT_NAME }}" \
-f env_description="${{ matrix.env_description }}" \
-f test_type="e2e" \
--repo woocommerce/woocommerce-test-reports

View File

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

View File

@ -1,6 +1,6 @@
{
"dev": true,
"filter": "^(?:config|react|react-dom|eslint|typescript|@typescript-eslint|@types/react|@wordpress|@types/wordpress__components|postcss).*$",
"filter": "^(?:config|react|react-dom|eslint|typescript|@typescript-eslint|@types/react|@wordpress|@types/wordpress__components|postcss|@types/node).*$",
"indent": "\t",
"overrides": true,
"peer": true,
@ -59,7 +59,7 @@
"packages": [
"**"
],
"pinVersion": "^4.8.3"
"pinVersion": "^4.9.5"
},
{
"dependencies": [
@ -154,6 +154,15 @@
"**"
],
"pinVersion": "^8.4.7"
},
{
"dependencies": [
"@types/node"
],
"packages": [
"**"
],
"pinVersion": "^16.18.18"
}
]
}

View File

@ -0,0 +1,3 @@
// Import the default config file and expose it in the project root.
// Useful for editor integrations.
module.exports = require( '@wordpress/prettier-config' );

View File

@ -1,5 +1,78 @@
== Changelog ==
= 7.5.1 2023-03-21 =
**WooCommerce**
* Fix - Fix no enforcing of min/max limits in quantity selector of variable products. [#36871](https://github.com/woocommerce/woocommerce/pull/36871)
* Dev - Update column definitions with synonymous types to prevent dbDelta from trying to ALTER them on each install. [#37277](https://github.com/woocommerce/woocommerce/pull/37277)
* Update - Update WooCommerce Blocks to 9.6.6. [#37298](https://github.com/woocommerce/woocommerce/pull/37298)
= 7.5.0 2023-03-14 =
**WooCommerce**
* Fix - Add HPOS support to the reserved stock query [#36535](https://github.com/woocommerce/woocommerce/pull/36535)
* Fix - Comment: Fix inconsistencies on Analytics > Orders table when using date_paid or date_completed [#36876](https://github.com/woocommerce/woocommerce/pull/36876)
* Fix - Define a public `api` property in the WooCommerce class to prevent a PHP deprecation warning [#36545](https://github.com/woocommerce/woocommerce/pull/36545)
* Fix - Don't delete order from posts table when deleted from orders table if the later is authoritative and sync is off [#36617](https://github.com/woocommerce/woocommerce/pull/36617)
* Fix - Eliminate data store internal meta keys duplicates [#36611](https://github.com/woocommerce/woocommerce/pull/36611)
* Fix - Ensure changes made via the `woocommerce_order_list_table_prepare_items_query_args` are observed. [#36649](https://github.com/woocommerce/woocommerce/pull/36649)
* Fix - Ensuring that we know if allowTracking is true before adding exit page. [#36656](https://github.com/woocommerce/woocommerce/pull/36656)
* Fix - Fix Ampersand changed to &amp on product attribute export [#36525](https://github.com/woocommerce/woocommerce/pull/36525)
* Fix - Fix decimal points for NOK currency [#36780](https://github.com/woocommerce/woocommerce/pull/36780)
* Fix - Fix inconsitent product task icon colors [#36889](https://github.com/woocommerce/woocommerce/pull/36889)
* Fix - Fix WordPress unit tests libraries being installed in a symlinked folder structure [#36641](https://github.com/woocommerce/woocommerce/pull/36641)
* Fix - Make states optional for Hungary and Bulgaria. [#36701](https://github.com/woocommerce/woocommerce/pull/36701)
* Fix - Screen ID matching switched to untranslated 'woocommerce' strings. [#36854](https://github.com/woocommerce/woocommerce/pull/36854)
* Fix - Translate the labels for units of measure. [#36708](https://github.com/woocommerce/woocommerce/pull/36708)
* Fix - Update `config@3.3.7` (from `3.3.3`). Fix `node_env_var_name is not defined` error. [#33828](https://github.com/woocommerce/woocommerce/pull/33828)
* Add - Add 'add_tab' method in FormFactory to allow plugins to extend the WooCommerce admin product form [#36583](https://github.com/woocommerce/woocommerce/pull/36583)
* Add - Add @woocommerce/product-editor dependency and change dependency of ProductSectionLayout component. [#36600](https://github.com/woocommerce/woocommerce/pull/36600)
* Add - Add additional global attributes and local attributes information when saving product attributes [#36858](https://github.com/woocommerce/woocommerce/pull/36858)
* Add - Add a new Channels card in multichannel marketing page. [#36541](https://github.com/woocommerce/woocommerce/pull/36541)
* Add - Add an experimental slot for marketing overview extensibility [#36828](https://github.com/woocommerce/woocommerce/pull/36828)
* Add - Add slot fill support for tabs for the new product management MVP. [#36551](https://github.com/woocommerce/woocommerce/pull/36551)
* Add - Add survey after disabling new experience [#36544](https://github.com/woocommerce/woocommerce/pull/36544)
* Add - Add unique sku option to error data when setting product sku [#36612](https://github.com/woocommerce/woocommerce/pull/36612)
* Add - Add WC-specific criteria to the Site Health test for persistent object caches [#35202](https://github.com/woocommerce/woocommerce/pull/35202)
* Add - Enable new experience when new user selects "Physical product". [#36406](https://github.com/woocommerce/woocommerce/pull/36406)
* Update - Update WooCommerce Blocks to 9.6.5 [#37051](https://github.com/woocommerce/woocommerce/pull/37051)
* Update - Update WooCommerce Blocks to 9.6.3 [#36992](https://github.com/woocommerce/woocommerce/pull/36992)
* Update - Update WooCommerce Blocks to 9.6.2 [#36919](https://github.com/woocommerce/woocommerce/pull/36919)
* Update - Add date_paid and date_completed date sorting options for Revenue and Order reports [#36492](https://github.com/woocommerce/woocommerce/pull/36492)
* Update - Add default value for backorders [#36607](https://github.com/woocommerce/woocommerce/pull/36607)
* Update - Add Skydropx, Envia, Sendcloud, Packlink to shipping task [#36873](https://github.com/woocommerce/woocommerce/pull/36873)
* Update - Always show comments for product feedback form [#36484](https://github.com/woocommerce/woocommerce/pull/36484)
* Update - Delete FlexSlider code for legacy browsers. [#36690](https://github.com/woocommerce/woocommerce/pull/36690)
* Update - Disable the new product editor, pending design updates. [#36894](https://github.com/woocommerce/woocommerce/pull/36894)
* Update - Have "Grow your store" appear first in marketing task by default [#36826](https://github.com/woocommerce/woocommerce/pull/36826)
* Update - Migrating product editor pricing section to slot fills. [#36500](https://github.com/woocommerce/woocommerce/pull/36500)
* Update - Refactor slot fills to ensure variant fills have distinct slots. [#36646](https://github.com/woocommerce/woocommerce/pull/36646)
* Update - Removed I.D column from product import samples [#36857](https://github.com/woocommerce/woocommerce/pull/36857)
* Update - Remove Meta from grow your store list [#36886](https://github.com/woocommerce/woocommerce/pull/36886)
* Update - Remove opinionated styles from buttons in block themes so they inherit theme styles more accurately [#36651](https://github.com/woocommerce/woocommerce/pull/36651)
* Update - Replace $.ajax() calls with browser-native window.fetch() calls. [#36275](https://github.com/woocommerce/woocommerce/pull/36275)
* Update - Update payment gateway list ordering priority and remove Klarna from North America [#36550](https://github.com/woocommerce/woocommerce/pull/36550)
* Update - Update Playwright version from 1.28.0 -> 1.30.0 [#36789](https://github.com/woocommerce/woocommerce/pull/36789)
* Update - Updates to product editor fill to support new prop API. [#36592](https://github.com/woocommerce/woocommerce/pull/36592)
* Update - Update WooCommerce Blocks 9.6.0 & 9.6.1 [#36852](https://github.com/woocommerce/woocommerce/pull/36852)
* Dev - Add attribute creation form when there are no attributes [#36606](https://github.com/woocommerce/woocommerce/pull/36606)
* Dev - Add a unit test for woocommerce_admin_experimental_onboarding_tasklists filter [#36827](https://github.com/woocommerce/woocommerce/pull/36827)
* Dev - Code refactor on marketing components. [#36540](https://github.com/woocommerce/woocommerce/pull/36540)
* Dev - Made e2e selectors more robust [#36499](https://github.com/woocommerce/woocommerce/pull/36499)
* Dev - Remove attribute type logic from attribute component [#36563](https://github.com/woocommerce/woocommerce/pull/36563)
* Dev - Update eslint to 8.32.0 across the monorepo. [#36700](https://github.com/woocommerce/woocommerce/pull/36700)
* Dev - Update pnpm command to run e2e tests for consistency. Also update docs with new command. [#35287](https://github.com/woocommerce/woocommerce/pull/35287)
* Tweak - Add IR and fields priorities to list of get_country_locale() method to follow conventional way of addressing in Iran. [#36491](https://github.com/woocommerce/woocommerce/pull/36491)
* Tweak - Add missing deprecation notice for filter hook woocommerce_my_account_my_orders_columns. [#36356](https://github.com/woocommerce/woocommerce/pull/36356)
* Tweak - Adjust default sizes for the quantity and coupon input fields within the cart page. [#29122](https://github.com/woocommerce/woocommerce/pull/29122)
* Tweak - Do not display low/out-of-stock information in the dashboard status widget when stock management is disabled. [#36703](https://github.com/woocommerce/woocommerce/pull/36703)
* Tweak - Remove free trial terms from Avalara tax task [#36888](https://github.com/woocommerce/woocommerce/pull/36888)
* Tweak - Tweak product link description and display in the new product management experience [#36591](https://github.com/woocommerce/woocommerce/pull/36591)
* Enhancement - Change the sass variable names to more predictable ones. [#28908](https://github.com/woocommerce/woocommerce/pull/28908)
= 7.4.1 2023-03-01 =
**WooCommerce**

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Fix the incorrect workflow input for uploading Allure reports.

View File

@ -3,6 +3,10 @@
"title": "WooCommerce Monorepo",
"description": "Monorepo for the WooCommerce ecosystem",
"homepage": "https://woocommerce.com/",
"engines": {
"node": "^16.13.1",
"pnpm": "^7.13.3"
},
"private": true,
"repository": {
"type": "git",
@ -25,41 +29,41 @@
"sync-dependencies": "pnpm exec syncpack -- fix-mismatches"
},
"devDependencies": {
"@babel/preset-env": "^7.16.11",
"@babel/runtime": "^7.17.2",
"@types/node": "14.14.33",
"@babel/preset-env": "^7.20.2",
"@babel/runtime": "^7.21.0",
"@types/node": "^16.18.18",
"@woocommerce/eslint-plugin": "workspace:*",
"@wordpress/data": "wp-6.0",
"@wordpress/eslint-plugin": "^11.1.0",
"@wordpress/prettier-config": "^1.1.1",
"babel-loader": "^8.2.3",
"@wordpress/prettier-config": "^1.4.0",
"babel-loader": "^8.3.0",
"chalk": "^4.1.2",
"copy-webpack-plugin": "^10.2.4",
"core-js": "^3.21.1",
"css-loader": "^6.7.0",
"glob": "^7.2.0",
"core-js": "^3.29.1",
"css-loader": "^6.7.3",
"glob": "^7.2.3",
"husky": "^7.0.4",
"jest": "^27.3.1",
"lint-staged": "^12.3.7",
"jest": "^27.5.1",
"lint-staged": "^12.5.0",
"mkdirp": "^1.0.4",
"moment": "^2.29.1",
"moment": "^2.29.4",
"node-stream-zip": "^1.15.0",
"postcss-loader": "^4.3.0",
"prettier": "npm:wp-prettier@^2.2.1-beta-1",
"regenerator-runtime": "^0.13.9",
"prettier": "npm:wp-prettier@^2.6.2",
"regenerator-runtime": "^0.13.11",
"request": "^2.88.2",
"sass": "^1.49.9",
"sass-loader": "^10.2.1",
"sass": "^1.59.3",
"sass-loader": "^10.4.1",
"syncpack": "^9.8.4",
"turbo": "^1.7.0",
"typescript": "^4.8.3",
"turbo": "^1.8.5",
"typescript": "^4.9.5",
"url-loader": "^1.1.2",
"webpack": "^5.70.0"
"webpack": "^5.76.2"
},
"dependencies": {
"@babel/core": "7.12.9",
"@wordpress/babel-plugin-import-jsx-pragma": "^3.1.0",
"@wordpress/babel-preset-default": "^6.4.1",
"@wordpress/babel-plugin-import-jsx-pragma": "^3.2.0",
"@wordpress/babel-preset-default": "^6.17.0",
"lodash": "^4.17.21",
"wp-textdomain": "1.0.1"
},

View File

@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: TypeScript build change

View File

@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: Configuration change only

View File

@ -0,0 +1,4 @@
Significance: patch
Type: update
Update showOtherPaymentMethods() to test latest payment task properly

View File

@ -50,18 +50,18 @@
"jest-mock-extended": "^1.0.18",
"rimraf": "^3.0.2",
"ts-jest": "^27.1.3",
"typescript": "^4.8.3"
"typescript": "^4.9.5"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"turbo:build": "tsc --build",
"turbo:build": "tsc --project tsconfig.json",
"prepare": "composer install",
"changelog": "composer exec -- changelogger",
"build": "pnpm -w exec turbo run turbo:build --filter=$npm_package_name",
"lint": "eslint src",
"start": "tsc --build --watch",
"start": "tsc --project tsconfig.json --watch",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"lint:fix": "eslint src --fix",
"prepack": "pnpm run clean && pnpm run build"

View File

@ -31,12 +31,6 @@ export class PaymentsSetup extends BasePage {
}
async showOtherPaymentMethods(): Promise< void > {
const selector = '.woocommerce-task-payments button.toggle-button';
await this.page.waitForSelector( selector );
const toggleButton = await this.page.$(
`${ selector }[aria-expanded=false]`
);
await toggleButton?.click();
await waitForElementByText( 'h2', 'Offline payment methods' );
}

View File

@ -1,8 +1,11 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"composite": true,
"rootDir": "src",
"outDir": "build"
"outDir": "build",
"typeRoots": [
"./typings",
"./node_modules/@types"
]
}
}

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Adding support for modifying fill name to WooHeaderItem.

View File

@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: TypeScript build change

View File

@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: Configuration change only

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update webpack config to use @woocommerce/internal-style-build's parser config

View File

@ -34,9 +34,9 @@
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"build": "pnpm -w exec turbo run turbo:build --filter=$npm_package_name",
"lint": "eslint src",
"build:js": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
"build:js": "tsc --project tsconfig.json && tsc --project tsconfig-cjs.json",
"build:css": "webpack",
"start": "concurrently \"tsc --build --watch\" \"webpack --watch\"",
"start": "concurrently \"tsc --project tsconfig.json --watch\" \"tsc --project tsconfig-cjs.json --watch\" \"webpack --watch\"",
"prepack": "pnpm run clean && pnpm run build",
"lint:fix": "eslint src --fix"
},
@ -50,12 +50,13 @@
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"concurrently": "^7.0.0",
"postcss-loader": "^4.3.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"sass-loader": "^10.2.1",
"ts-jest": "^27.1.3",
"typescript": "^4.8.3",
"typescript": "^4.9.5",
"webpack": "^5.70.0",
"webpack-cli": "^3.3.12"
},

View File

@ -11,6 +11,20 @@ import {
export const WC_HEADER_SLOT_NAME = 'woocommerce_header_item';
/**
* Get the slot fill name for the generic header slot or a specific header if provided.
*
* @param name Name of the specific header.
* @return string
*/
const getSlotFillName = ( name?: string ) => {
if ( ! name || ! name.length ) {
return WC_HEADER_SLOT_NAME;
}
return `${ WC_HEADER_SLOT_NAME }/${ name }`;
};
/**
* Create a Fill for extensions to add items to the WooCommerce Admin header.
*
@ -26,17 +40,19 @@ export const WC_HEADER_SLOT_NAME = 'woocommerce_header_item';
* scope: 'woocommerce-admin',
* } );
* @param {Object} param0
* @param {Array} param0.name - Header name.
* @param {Array} param0.children - Node children.
* @param {Array} param0.order - Node order.
*/
export const WooHeaderItem: React.FC< {
name?: string;
children?: React.ReactNode;
order?: number;
} > & {
Slot: React.FC< Slot.Props >;
} = ( { children, order = 1 } ) => {
Slot: React.FC< Slot.Props & { name?: string } >;
} = ( { children, order = 1, name = '' } ) => {
return (
<Fill name={ WC_HEADER_SLOT_NAME }>
<Fill name={ getSlotFillName( name ) }>
{ ( fillProps: Fill.Props ) => {
return createOrderedChildren( children, order, fillProps );
} }
@ -44,8 +60,8 @@ export const WooHeaderItem: React.FC< {
);
};
WooHeaderItem.Slot = ( { fillProps } ) => (
<Slot name={ WC_HEADER_SLOT_NAME } fillProps={ fillProps }>
WooHeaderItem.Slot = ( { fillProps, name = '' } ) => (
<Slot name={ getSlotFillName( name ) } fillProps={ fillProps }>
{ sortFillsByOrder }
</Slot>
);

View File

@ -1,6 +1,10 @@
{
"extends": "../tsconfig-cjs",
"compilerOptions": {
"outDir": "build"
"outDir": "build",
"typeRoots": [
"./typings",
"./node_modules/@types"
]
}
}

View File

@ -5,6 +5,10 @@
"outDir": "build-module",
"declaration": true,
"declarationMap": true,
"declarationDir": "./build-types"
"declarationDir": "./build-types",
"typeRoots": [
"./typings",
"./node_modules/@types"
]
}
}

View File

@ -12,6 +12,7 @@ module.exports = {
path: __dirname,
},
module: {
parser: webpackConfig.parser,
rules: webpackConfig.rules,
},
plugins: webpackConfig.plugins,

View File

@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: TypeScript build change

View File

@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: Configuration change only

View File

@ -36,7 +36,7 @@
"prepare": "composer install",
"changelog": "composer exec -- changelogger",
"clean": "rm -rf ./dist ./tsconfig.tsbuildinfo",
"compile": "tsc -b",
"compile": "tsc --project tsconfig.json",
"prepack": "pnpm run build",
"build": "pnpm -w exec turbo run turbo:build --filter=$npm_package_name",
"lint": "eslint src",
@ -50,7 +50,7 @@
"devDependencies": {
"@types/create-hmac": "1.1.0",
"@types/jest": "^27.4.1",
"@types/node": "13.13.5",
"@types/node": "^16.18.18",
"@typescript-eslint/eslint-plugin": "^5.54.0",
"@typescript-eslint/parser": "^5.54.0",
"@woocommerce/eslint-plugin": "workspace:*",
@ -58,7 +58,7 @@
"eslint": "^8.32.0",
"jest": "^27",
"ts-jest": "^27",
"typescript": "^4.8.3"
"typescript": "^4.9.5"
},
"publishConfig": {
"access": "public"

View File

@ -4,7 +4,11 @@
"types": [ "node", "jest", "axios", "create-hmac" ],
"rootDir": "src",
"outDir": "dist",
"target": "es5"
"target": "es5",
"typeRoots": [
"./typings",
"./node_modules/@types"
]
},
"include": [ "src/" ]
}

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Adding simple DisplayState wrapper and modifying Collapsible component to allow rendering hidden content.

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Create SelectTree component that uses TreeControl

View File

@ -0,0 +1,4 @@
Significance: minor
Type: fix
Prevent duplicate registration of core blocks in client

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update TourKit README to correct primaryButton example and formatting.

View File

@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: TypeScript build change

View File

@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: Configuration change only

View File

@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: Configuration change only

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Apply wccom experimental select control changes

View File

@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: Add unit tests

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Update webpack config to use @woocommerce/internal-style-build's parser config

View File

@ -1,4 +1,4 @@
{
"rootDir": "./src",
"preset": "../../internal-js-tests/jest.config.js"
"preset": "../node_modules/@woocommerce/internal-js-tests/jest-preset.js"
}

View File

@ -128,6 +128,7 @@
"@types/wordpress__viewport": "^2.5.4",
"@woocommerce/eslint-plugin": "workspace:*",
"@woocommerce/internal-style-build": "workspace:*",
"@woocommerce/internal-js-tests": "workspace:*",
"@wordpress/browserslist-config": "wp-6.0",
"@wordpress/scripts": "^12.6.1",
"concurrently": "^7.0.0",
@ -141,7 +142,7 @@
"rimraf": "^3.0.2",
"sass-loader": "^10.2.1",
"ts-jest": "^27.1.3",
"typescript": "^4.8.3",
"typescript": "^4.9.5",
"uuid": "^8.3.0",
"webpack": "^5.70.0",
"webpack-cli": "^3.3.12"
@ -154,12 +155,12 @@
"build": "pnpm -w exec turbo run turbo:build --filter=$npm_package_name",
"test": "pnpm -w exec turbo run turbo:test --filter=$npm_package_name",
"lint": "eslint src --ext=js,ts,tsx",
"build:js": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
"build:js": "tsc --project tsconfig.json && tsc --project tsconfig-cjs.json",
"build:css": "webpack",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"lint:fix": "eslint src --ext=js,ts,tsx --fix",
"prepack": "pnpm run clean && pnpm run build",
"start": "concurrently \"tsc --build ./tsconfig.json --watch\" \"webpack --watch\"",
"start": "concurrently \"tsc --project tsconfig.json --watch\" \"tsc --project tsconfig-cjs.json --watch\" \"webpack --watch\"",
"test:update-snapshots": "pnpm run test -- --updateSnapshot",
"test-staged": "jest --bail --config ./jest.config.json --findRelatedTests"
},

View File

@ -7,10 +7,12 @@ import { Icon, chevronDown, chevronUp } from '@wordpress/icons';
/**
* Internal dependencies
*/
import { DisplayState } from '../display-state';
export type CollapsedProps = {
initialCollapsed?: boolean;
toggleText: string;
persistRender?: boolean;
children: React.ReactNode;
} & React.HTMLAttributes< HTMLDivElement >;
@ -18,9 +20,19 @@ export const CollapsibleContent: React.FC< CollapsedProps > = ( {
initialCollapsed = true,
toggleText,
children,
persistRender = false,
...props
}: CollapsedProps ) => {
const [ collapsed, setCollapsed ] = useState( initialCollapsed );
const getState = () => {
if ( ! collapsed ) {
return 'visible';
}
return persistRender ? 'visually-hidden' : 'hidden';
};
return (
<div
aria-expanded={ collapsed ? 'false' : 'true' }
@ -38,14 +50,14 @@ export const CollapsibleContent: React.FC< CollapsedProps > = ( {
/>
</div>
</button>
{ ! collapsed && (
<DisplayState state={ getState() }>
<div
{ ...props }
className="woocommerce-collapsible-content__content"
>
{ children }
</div>
) }
</DisplayState>
</div>
);
};

View File

@ -0,0 +1,28 @@
/**
* External dependencies
*/
import { createElement, Fragment } from '@wordpress/element';
/**
* Internal dependencies
*/
export type DisplayStateProps = {
state?: 'visible' | 'visually-hidden' | 'hidden';
children: React.ReactNode;
} & React.HTMLAttributes< HTMLDivElement >;
export const DisplayState: React.FC< DisplayStateProps > = ( {
state = 'visible',
children,
} ) => {
if ( state === 'visible' ) {
return <>{ children }</>;
}
if ( state === 'visually-hidden' ) {
return <div style={ { display: 'none' } }>{ children }</div>;
}
return null;
};

View File

@ -0,0 +1 @@
export * from './display-state';

View File

@ -44,3 +44,18 @@
.woocommerce-experimental-select-control__suffix {
align-self: stretch;
}
.woocommerce-experimental-select-control__combox-box-toggle-button {
all: unset;
position: absolute;
right: 6px;
top: 50%;
transform: translateY( -50% );
> svg {
margin-top: 4px;
}
}
.woocommerce-experimental-select-control:not( .is-focused ) .woocommerce-experimental-select-control__combox-box-toggle-button {
pointer-events: none; // Prevents the icon from being clickable when the combobox is not focused, because otherwise we get a race condition when clicking on the icon, because focussing the combobox opens the menu, then sequentially the icon toggles it back closed
}

View File

@ -1,26 +1,42 @@
/**
* External dependencies
*/
import { createElement, MouseEvent, useRef } from 'react';
import { createElement, MouseEvent, useRef, forwardRef } from 'react';
import classNames from 'classnames';
/**
* Internal dependencies
*/
import { Props } from './types';
import { Icon, chevronDown } from '@wordpress/icons';
type ComboBoxProps = {
children?: JSX.Element | JSX.Element[] | null;
comboBoxProps: Props;
inputProps: Props;
comboBoxProps: JSX.IntrinsicElements[ 'div' ];
inputProps: JSX.IntrinsicElements[ 'input' ];
getToggleButtonProps?: () => Omit<
JSX.IntrinsicElements[ 'button' ],
'ref'
>;
suffix?: JSX.Element | null;
showToggleButton?: boolean;
};
const ToggleButton = forwardRef< HTMLButtonElement >( ( props, ref ) => {
// using forwardRef here because getToggleButtonProps injects a ref prop
return (
<button
className="woocommerce-experimental-select-control__combox-box-toggle-button"
{ ...props }
ref={ ref }
>
<Icon icon={ chevronDown } />
</button>
);
} );
export const ComboBox = ( {
children,
comboBoxProps,
getToggleButtonProps = () => ( {} ),
inputProps,
suffix,
showToggleButton,
}: ComboBoxProps ) => {
const inputRef = useRef< HTMLInputElement | null >( null );
@ -60,12 +76,14 @@ export const ComboBox = ( {
<input
{ ...inputProps }
ref={ ( node ) => {
inputRef.current = node;
(
inputProps.ref as unknown as (
node: HTMLInputElement | null
) => void
)( node );
if ( typeof inputProps.ref === 'function' ) {
inputRef.current = node;
(
inputProps.ref as unknown as (
node: HTMLInputElement | null
) => void
)( node );
}
} }
/>
</div>
@ -75,6 +93,9 @@ export const ComboBox = ( {
{ suffix }
</div>
) }
{ showToggleButton && (
<ToggleButton { ...getToggleButtonProps() } />
) }
</div>
);
};

View File

@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { createElement, ReactElement } from 'react';
import { createElement, CSSProperties, ReactElement } from 'react';
/**
* Internal dependencies
@ -14,6 +14,7 @@ export type MenuItemProps< ItemType > = {
item: ItemType;
children: ReactElement | string;
getItemProps: getItemPropsType< ItemType >;
activeStyle?: CSSProperties;
};
export const MenuItem = < ItemType, >( {
@ -21,11 +22,12 @@ export const MenuItem = < ItemType, >( {
getItemProps,
index,
isActive,
activeStyle = { backgroundColor: '#bde4ff' },
item,
}: MenuItemProps< ItemType > ) => {
return (
<li
style={ isActive ? { backgroundColor: '#bde4ff' } : {} }
style={ isActive ? activeStyle : {} }
{ ...getItemProps( { item, index } ) }
className="woocommerce-experimental-select-control__menu-item"
>

View File

@ -22,6 +22,8 @@ type MenuProps = {
getMenuProps: getMenuPropsType;
isOpen: boolean;
className?: string;
position?: Popover.Position;
scrollIntoViewOnOpen?: boolean;
};
export const Menu = ( {
@ -29,6 +31,8 @@ export const Menu = ( {
getMenuProps,
isOpen,
className,
position = 'bottom right',
scrollIntoViewOnOpen = false,
}: MenuProps ) => {
const [ boundingRect, setBoundingRect ] = useState< DOMRect >();
const selectControlMenuRef = useRef< HTMLDivElement >( null );
@ -41,6 +45,13 @@ export const Menu = ( {
}
}, [ selectControlMenuRef.current ] );
// Scroll the selected item into view when the menu opens.
useEffect( () => {
if ( isOpen && scrollIntoViewOnOpen ) {
selectControlMenuRef.current?.scrollIntoView();
}
}, [ isOpen, scrollIntoViewOnOpen ] );
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events */
/* Disabled because of the onmouseup on the ul element below. */
return (
@ -60,7 +71,7 @@ export const Menu = ( {
'has-results': Children.count( children ) > 0,
}
) }
position="bottom right"
position={ position }
animate={ false }
>
<ul

View File

@ -68,6 +68,7 @@ export type SelectControlProps< ItemType > = {
disabled?: boolean;
inputProps?: GetInputPropsOptions;
suffix?: JSX.Element | null;
showToggleButton?: boolean;
/**
* This is a feature already implemented in downshift@7.0.0 through the
* reducer. In order for us to use it this prop is added temporarily until
@ -123,6 +124,7 @@ function SelectControl< ItemType = DefaultItemType >( {
disabled,
inputProps = {},
suffix = <SuffixIcon icon={ search } />,
showToggleButton = false,
__experimentalOpenMenuOnFocus = false,
}: SelectControlProps< ItemType > ) {
const [ isFocused, setIsFocused ] = useState( false );
@ -154,12 +156,13 @@ function SelectControl< ItemType = DefaultItemType >( {
}
setInputValue( getItemLabel( singleSelectedItem ) );
}, [ singleSelectedItem ] );
}, [ getItemLabel, multiple, singleSelectedItem ] );
const {
isOpen,
getLabelProps,
getMenuProps,
getToggleButtonProps,
getInputProps,
getComboboxProps,
highlightedIndex,
@ -256,6 +259,7 @@ function SelectControl< ItemType = DefaultItemType >( {
{ /* eslint-enable jsx-a11y/label-has-for */ }
<ComboBox
comboBoxProps={ getComboboxProps() }
getToggleButtonProps={ getToggleButtonProps }
inputProps={ getInputProps( {
...getDropdownProps( {
preventKeyAction: isOpen,
@ -274,6 +278,7 @@ function SelectControl< ItemType = DefaultItemType >( {
...inputProps,
} ) }
suffix={ suffix }
showToggleButton={ showToggleButton }
>
<>
{ children( {

View File

@ -573,6 +573,24 @@ export const CustomSuffix: React.FC = () => {
);
};
export const ToggleButton: React.FC = () => {
const [ selected, setSelected ] =
useState< SelectedType< DefaultItemType > >();
return (
<SelectControl
items={ sampleItems }
label="Has toggle button"
selected={ selected }
onSelect={ ( item ) => item && setSelected( item ) }
onRemove={ () => setSelected( null ) }
suffix={ null }
showToggleButton={ true }
__experimentalOpenMenuOnFocus={ true }
/>
);
};
export default {
title: 'WooCommerce Admin/experimental/SelectControl',
component: SelectControl,

View File

@ -16,7 +16,8 @@ export type DefaultItemType = {
export type SelectedType< ItemType > = ItemType | null;
export type Props = {
[ key: string ]: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[ key: string ]: any;
};
export type getItemPropsType< ItemType > = (

View File

@ -0,0 +1 @@
export * from './select-tree';

View File

@ -0,0 +1,15 @@
.woocommerce-experimental-select-control__combo-box-wrapper {
&:focus {
box-shadow: 0 0 0 1px var( --wp-admin-theme-color );
border-color: var( --wp-admin-theme-color );
}
}
.woocommerce-experimental-select-tree-control__dropdown {
display: block;
}
// That's the only way I could remove the padding from the @wordpress/components Dropdown.
.woocommerce-experimental-select-tree-control__dropdown-content .components-popover__content {
padding: 0;
}

View File

@ -0,0 +1,199 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* External dependencies
*/
import { createElement, useRef, useState } from 'react';
import classNames from 'classnames';
import { search } from '@wordpress/icons';
import { Dropdown, Spinner } from '@wordpress/components';
/**
* Internal dependencies
*/
import { useLinkedTree } from '../experimental-tree-control/hooks/use-linked-tree';
import { Tree } from '../experimental-tree-control/tree';
import {
Item,
LinkedTree,
TreeControlProps,
} from '../experimental-tree-control/types';
import { SelectedItems } from '../experimental-select-control/selected-items';
import { ComboBox } from '../experimental-select-control/combo-box';
import { SuffixIcon } from '../experimental-select-control/suffix-icon';
interface SelectTreeProps extends TreeControlProps {
id: string;
selected?: Item[];
getSelectedItemProps?: any;
treeRef?: React.ForwardedRef< HTMLOListElement >;
suffix?: JSX.Element | null;
isLoading?: boolean;
label: string | JSX.Element;
onInputChange?: ( value: string | undefined ) => void;
}
export const SelectTree = function SelectTree( {
items,
getSelectedItemProps,
treeRef: ref,
suffix = <SuffixIcon icon={ search } />,
placeholder,
isLoading,
onInputChange,
shouldShowCreateButton,
...props
}: SelectTreeProps ) {
const linkedTree = useLinkedTree( items );
const [ isFocused, setIsFocused ] = useState( false );
const comboBoxRef = useRef< HTMLDivElement >( null );
// getting the parent's parent div width to set the width of the dropdown
const comboBoxWidth =
comboBoxRef.current?.parentElement?.parentElement?.getBoundingClientRect()
.width;
const shouldItemBeExpanded = ( item: LinkedTree ): boolean => {
if ( ! props.createValue || ! item.children?.length ) return false;
return item.children.some( ( child ) => {
if (
new RegExp( props.createValue || '', 'ig' ).test(
child.data.label
)
) {
return true;
}
return shouldItemBeExpanded( child );
} );
};
return (
<Dropdown
className="woocommerce-experimental-select-tree-control__dropdown"
contentClassName="woocommerce-experimental-select-tree-control__dropdown-content"
focusOnMount={ false }
renderContent={ ( { onClose } ) =>
isLoading ? (
<div
style={ {
width: comboBoxWidth,
} }
>
<Spinner />
</div>
) : (
<Tree
{ ...props }
id={ `${ props.id }-menu` }
ref={ ref }
items={ linkedTree }
onTreeBlur={ onClose }
shouldItemBeExpanded={ shouldItemBeExpanded }
shouldShowCreateButton={ shouldShowCreateButton }
style={ {
width: comboBoxWidth,
} }
/>
)
}
renderToggle={ ( { isOpen, onToggle, onClose } ) => (
<div
className={ classNames(
'woocommerce-experimental-select-control',
{
'is-focused': isFocused,
}
) }
>
<label
htmlFor={ `${ props.id }-input` }
id={ `${ props.id }-label` }
className="woocommerce-experimental-select-control__label"
>
{ props.label }
</label>
<ComboBox
comboBoxProps={ {
className:
'woocommerce-experimental-select-control__combo-box-wrapper',
ref: comboBoxRef,
role: 'combobox',
'aria-expanded': isOpen,
'aria-haspopup': 'tree',
'aria-labelledby': `${ props.id }-label`,
'aria-owns': `${ props.id }-menu`,
} }
inputProps={ {
className:
'woocommerce-experimental-select-control__input',
id: `${ props.id }-input`,
'aria-autocomplete': 'list',
'aria-controls': `${ props.id }-menu`,
autoComplete: 'off',
onFocus: () => {
if ( ! isOpen ) {
onToggle();
}
setIsFocused( true );
},
onBlur: ( event ) => {
// if blurring to an element inside the dropdown, don't close it
if (
isOpen &&
! document
.querySelector(
'.woocommerce-experimental-select-control ~ .components-popover'
)
?.contains( event.relatedTarget )
) {
onClose();
}
setIsFocused( false );
},
onKeyDown: ( event ) => {
const baseQuery =
'.woocommerce-experimental-select-tree-control__dropdown > .components-popover';
if ( event.key === 'ArrowDown' ) {
event.preventDefault();
// focus on the first element from the Popover
(
document.querySelector(
`${ baseQuery } input, ${ baseQuery } button`
) as
| HTMLInputElement
| HTMLButtonElement
)?.focus();
}
if ( event.key === 'Tab' ) {
onClose();
}
},
onChange: ( event ) =>
onInputChange &&
onInputChange( event.target.value ),
placeholder,
} }
suffix={ suffix }
>
<SelectedItems
items={ ( props.selected as Item[] ) || [] }
getItemLabel={ ( item ) => item?.label || '' }
getItemValue={ ( item ) => item?.value || '' }
onRemove={ ( item ) => {
if (
! Array.isArray( item ) &&
props.onRemove
) {
props.onRemove( item );
onClose();
}
} }
getSelectedItemProps={ () => ( {} ) }
/>
</ComboBox>
</div>
) }
/>
);
};

View File

@ -0,0 +1,101 @@
/**
* External dependencies
*/
import React, { createElement } from 'react';
/**
* Internal dependencies
*/
import { SelectTree } from '../select-tree';
import { Item } from '../../experimental-tree-control/types';
const listItems: Item[] = [
{ value: '1', label: 'Technology' },
{ value: '1.1', label: 'Notebooks', parent: '1' },
{ value: '1.2', label: 'Phones', parent: '1' },
{ value: '1.2.1', label: 'iPhone', parent: '1.2' },
{ value: '1.2.1.1', label: 'iPhone 14 Pro', parent: '1.2.1' },
{ value: '1.2.1.2', label: 'iPhone 14 Pro Max', parent: '1.2.1' },
{ value: '1.2.2', label: 'Samsung', parent: '1.2' },
{ value: '1.2.2.1', label: 'Samsung Galaxy 22 Plus', parent: '1.2.2' },
{ value: '1.2.2.2', label: 'Samsung Galaxy 22 Ultra', parent: '1.2.2' },
{ value: '1.3', label: 'Wearables', parent: '1' },
{ value: '2', label: 'Hardware' },
{ value: '2.1', label: 'CPU', parent: '2' },
{ value: '2.2', label: 'GPU', parent: '2' },
{ value: '2.3', label: 'Memory RAM', parent: '2' },
{ value: '3', label: 'Other' },
];
const filterItems = ( items: Item[], searchValue ) => {
const filteredItems = items.filter( ( e ) =>
e.label.includes( searchValue )
);
const itemsToIterate = [ ...filteredItems ];
while ( itemsToIterate.length > 0 ) {
// The filter should include the parents of the filtered items
const element = itemsToIterate.pop();
if ( element ) {
const parent = listItems.find(
( item ) => item.value === element.parent
);
if ( parent && ! filteredItems.includes( parent ) ) {
filteredItems.push( parent );
itemsToIterate.push( parent );
}
}
}
return filteredItems;
};
export const MultipleSelectTree: React.FC = () => {
const [ value, setValue ] = React.useState( '' );
const [ selected, setSelected ] = React.useState< Item[] >( [] );
const items = filterItems( listItems, value );
return (
<SelectTree
id="multiple-select-tree"
label="Multiple Select Tree"
multiple
items={ items }
selected={ selected }
shouldNotRecursivelySelect
shouldShowCreateButton={ ( typedValue ) =>
! value ||
listItems.findIndex( ( item ) => item.label === typedValue ) ===
-1
}
createValue={ value }
// eslint-disable-next-line no-alert
onCreateNew={ () => alert( 'create new called' ) }
onInputChange={ ( a ) => setValue( a || '' ) }
onSelect={ ( selectedItems ) => {
if ( Array.isArray( selectedItems ) ) {
setSelected( [ ...selected, ...selectedItems ] );
}
} }
onRemove={ ( removedItems ) => {
const newValues = Array.isArray( removedItems )
? selected.filter(
( item ) =>
! removedItems.some(
( { value: removedValue } ) =>
item.value === removedValue
)
)
: selected.filter(
( item ) => item.value !== removedItems.value
);
setSelected( newValues );
} }
/>
);
};
export default {
title: 'WooCommerce Admin/experimental/SelectTreeControl',
component: SelectTree,
};

View File

@ -0,0 +1,107 @@
import { render } from '@testing-library/react';
import React, { createElement } from '@wordpress/element';
import { SelectTree } from '../select-tree';
import { Item } from '../../experimental-tree-control';
const mockItems: Item[] = [
{
label: 'Item 1',
value: 'item-1',
},
{
label: 'Item 2',
value: 'item-2',
parent: 'item-1',
},
{
label: 'Item 3',
value: 'item-3',
},
];
const DEFAULT_PROPS = {
id: 'select-tree',
items: mockItems,
label: 'Select Tree',
placeholder: 'Type here',
};
describe( 'SelectTree', () => {
beforeEach( () => {
jest.clearAllMocks();
} );
it( 'should show the popover only when focused', () => {
const { queryByPlaceholderText, queryByText } = render(
<SelectTree { ...DEFAULT_PROPS } />
);
expect( queryByText( 'Item 1' ) ).not.toBeInTheDocument();
queryByPlaceholderText( 'Type here' )?.focus();
expect( queryByText( 'Item 1' ) ).toBeInTheDocument();
} );
it( 'should show create button when callback is true ', () => {
const { queryByText, queryByPlaceholderText } = render(
<SelectTree
{ ...DEFAULT_PROPS }
shouldShowCreateButton={ () => true }
/>
);
queryByPlaceholderText( 'Type here' )?.focus();
expect( queryByText( 'Create new' ) ).toBeInTheDocument();
} );
it( 'should not show create button when callback is false or no callback', () => {
const { queryByText, queryByPlaceholderText } = render(
<SelectTree { ...DEFAULT_PROPS } />
);
queryByPlaceholderText( 'Type here' )?.focus();
expect( queryByText( 'Create new' ) ).not.toBeInTheDocument();
} );
it( 'should show a root item when focused and child when expand button is clicked', () => {
const { queryByText, queryByLabelText, queryByPlaceholderText } =
render( <SelectTree { ...DEFAULT_PROPS } /> );
queryByPlaceholderText( 'Type here' )?.focus();
expect( queryByText( 'Item 1' ) ).toBeInTheDocument();
expect( queryByText( 'Item 2' ) ).not.toBeInTheDocument();
queryByLabelText( 'Expand' )?.click();
expect( queryByText( 'Item 2' ) ).toBeInTheDocument();
} );
it( 'should show selected items', () => {
const { queryAllByRole, queryByPlaceholderText } = render(
<SelectTree { ...DEFAULT_PROPS } selected={ [ mockItems[ 0 ] ] } />
);
queryByPlaceholderText( 'Type here' )?.focus();
expect( queryAllByRole( 'treeitem' )[ 0 ] ).toHaveAttribute(
'aria-selected',
'true'
);
} );
it( 'should show Create "<createValue>" button', () => {
const { queryByPlaceholderText, queryByText } = render(
<SelectTree
{ ...DEFAULT_PROPS }
createValue="new item"
shouldShowCreateButton={ () => true }
/>
);
queryByPlaceholderText( 'Type here' )?.focus();
expect( queryByText( 'Create "new item"' ) ).toBeInTheDocument();
} );
it( 'should call onCreateNew when Create "<createValue>" button is clicked', () => {
const mockFn = jest.fn();
const { queryByPlaceholderText, queryByText } = render(
<SelectTree
{ ...DEFAULT_PROPS }
createValue="new item"
shouldShowCreateButton={ () => true }
onCreateNew={ mockFn }
/>
);
queryByPlaceholderText( 'Type here' )?.focus();
queryByText( 'Create "new item"' )?.click();
expect( mockFn ).toBeCalledTimes( 1 );
} );
} );

View File

@ -17,7 +17,8 @@ export function useExpander( {
useEffect( () => {
if (
item.children?.length &&
typeof shouldItemBeExpanded === 'function'
typeof shouldItemBeExpanded === 'function' &&
! isExpanded
) {
setExpanded( shouldItemBeExpanded( item ) );
}

View File

@ -110,12 +110,14 @@ export function useKeyboard( {
onExpand,
onCollapse,
onToggleExpand,
onLastItemLoop,
}: {
item: LinkedTree;
isExpanded: boolean;
onExpand(): void;
onCollapse(): void;
onToggleExpand(): void;
onLastItemLoop?( event: React.KeyboardEvent< HTMLDivElement > ): void;
} ) {
function onKeyDown( event: React.KeyboardEvent< HTMLDivElement > ) {
if ( event.code === 'ArrowRight' ) {
@ -154,6 +156,9 @@ export function useKeyboard( {
event.code
);
element?.focus();
if ( event.code === 'ArrowDown' && ! element && onLastItemLoop ) {
onLastItemLoop( event );
}
}
if ( event.code === 'Home' ) {
@ -169,5 +174,5 @@ export function useKeyboard( {
}
}
return { onKeyDown };
return { onKeyDown, onLastItemLoop };
}

View File

@ -75,6 +75,7 @@ function hasSelectedSibblingChildren(
export function useSelection( {
item,
multiple,
shouldNotRecursivelySelect,
selected,
level,
index,
@ -89,6 +90,7 @@ export function useSelection( {
| 'index'
| 'onSelect'
| 'onRemove'
| 'shouldNotRecursivelySelect'
> ) {
const selectedItems = useMemo( () => {
if ( level === 1 && index === 0 ) {
@ -100,7 +102,11 @@ export function useSelection( {
const checkedStatus: CheckedStatus = useMemo( () => {
if ( item.data.value in selectedItems ) {
if ( multiple && isIndeterminate( selectedItems, item.children ) ) {
if (
multiple &&
! shouldNotRecursivelySelect &&
isIndeterminate( selectedItems, item.children )
) {
return 'indeterminate';
}
return 'checked';
@ -113,7 +119,7 @@ export function useSelection( {
if ( multiple ) {
value = [ item.data ];
if ( item.children.length ) {
if ( item.children.length && ! shouldNotRecursivelySelect ) {
value.push( ...getDeepChildren( item ) );
}
} else if ( item.children?.length ) {
@ -132,7 +138,7 @@ export function useSelection( {
function onSelectChildren( value: Item | Item[] ) {
if ( typeof onSelect !== 'function' ) return;
if ( multiple ) {
if ( multiple && ! shouldNotRecursivelySelect ) {
value = [ item.data, ...( value as Item[] ) ];
}
@ -142,7 +148,11 @@ export function useSelection( {
function onRemoveChildren( value: Item | Item[] ) {
if ( typeof onRemove !== 'function' ) return;
if ( multiple && item.children?.length ) {
if (
multiple &&
item.children?.length &&
! shouldNotRecursivelySelect
) {
const hasSelectedSibbling = hasSelectedSibblingChildren(
item.children,
value as Item[],

View File

@ -17,6 +17,7 @@ export function useTreeItem( {
item,
level,
multiple,
shouldNotRecursivelySelect,
selected,
index,
getLabel,
@ -24,6 +25,11 @@ export function useTreeItem( {
shouldItemBeHighlighted,
onSelect,
onRemove,
isExpanded,
onCreateNew,
shouldShowCreateButton,
onLastItemLoop,
onTreeBlur,
...props
}: TreeItemProps ) {
const nextLevel = level + 1;
@ -41,6 +47,7 @@ export function useTreeItem( {
index,
onSelect,
onRemove,
shouldNotRecursivelySelect,
} );
const highlighter = useHighlighter( {
@ -56,6 +63,7 @@ export function useTreeItem( {
const { onKeyDown } = useKeyboard( {
...expander,
onLastItemLoop,
item,
} );
@ -96,6 +104,7 @@ export function useTreeItem( {
getItemLabel: getLabel,
shouldItemBeExpanded,
shouldItemBeHighlighted,
shouldNotRecursivelySelect,
onSelect: selection.onSelectChildren,
onRemove: selection.onRemoveChildren,
},

View File

@ -8,7 +8,6 @@
import { TreeProps } from '../types';
export function useTree( {
ref,
items,
level = 1,
role = 'tree',
@ -19,6 +18,11 @@ export function useTree( {
shouldItemBeHighlighted,
onSelect,
onRemove,
shouldNotRecursivelySelect,
createValue,
onTreeBlur,
onCreateNew,
shouldShowCreateButton,
...props
}: TreeProps ) {
return {
@ -35,6 +39,7 @@ export function useTree( {
getLabel: getItemLabel,
shouldItemBeExpanded,
shouldItemBeHighlighted,
shouldNotRecursivelySelect,
onSelect,
onRemove,
},

View File

@ -12,4 +12,13 @@
border: 1px solid $gray-400;
border-radius: 2px;
}
&__button {
width: 100%;
&:hover,
&:focus-within {
outline: 1.5px solid var( --wp-admin-theme-color );
outline-offset: -1.5px;
background-color: $gray-100;
}
}
}

View File

@ -1,8 +1,12 @@
/**
* External dependencies
*/
import { Button, Icon } from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import classNames from 'classnames';
import { createElement, forwardRef } from 'react';
import { createElement, forwardRef, Fragment, useRef } from 'react';
import { plus } from '@wordpress/icons';
import { useMergeRefs } from '@wordpress/compose';
/**
* Internal dependencies
@ -13,31 +17,93 @@ import { TreeProps } from './types';
export const Tree = forwardRef( function ForwardedTree(
props: TreeProps,
ref: React.ForwardedRef< HTMLOListElement >
forwardedRef: React.ForwardedRef< HTMLOListElement >
) {
const rootListRef = useRef< HTMLOListElement >( null );
const ref = useMergeRefs( [ rootListRef, forwardedRef ] );
const { level, items, treeProps, treeItemProps } = useTree( {
...props,
ref,
} );
if ( ! items.length ) return null;
const isCreateButtonVisible =
props.shouldShowCreateButton &&
props.shouldShowCreateButton( props.createValue );
return (
<ol
{ ...treeProps }
className={ classNames(
treeProps.className,
'experimental-woocommerce-tree',
`experimental-woocommerce-tree--level-${ level }`
<>
<ol
{ ...treeProps }
className={ classNames(
treeProps.className,
'experimental-woocommerce-tree',
`experimental-woocommerce-tree--level-${ level }`
) }
>
{ items.map( ( child, index ) => (
<TreeItem
{ ...treeItemProps }
isExpanded={ props.isExpanded }
key={ child.data.value }
item={ child }
index={ index }
// Button ref is not working, so need to use CSS directly
onLastItemLoop={ () => {
(
rootListRef.current
?.closest( 'ol[role="tree"]' )
?.parentElement?.querySelector(
'.experimental-woocommerce-tree__button'
) as HTMLButtonElement
)?.focus();
} }
/>
) ) }
</ol>
{ isCreateButtonVisible && (
<Button
className="experimental-woocommerce-tree__button"
onClick={ () => {
if ( props.onCreateNew ) {
props.onCreateNew();
}
if ( props.onTreeBlur ) {
props.onTreeBlur();
}
} }
// Component's event type definition is not working
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onKeyDown={ ( event: any ) => {
if (
event.key === 'ArrowUp' ||
event.key === 'ArrowDown'
) {
event.preventDefault();
if ( event.key === 'ArrowUp' ) {
const allHeadings =
event.nativeEvent.srcElement.previousSibling.querySelectorAll(
'.experimental-woocommerce-tree-item > .experimental-woocommerce-tree-item__heading'
);
allHeadings[ allHeadings.length - 1 ]
?.querySelector(
'.experimental-woocommerce-tree-item__label'
)
?.focus();
}
}
} }
>
<Icon icon={ plus } size={ 20 } />
{ props.createValue
? sprintf(
__( 'Create "%s"', 'woocommerce' ),
props.createValue
)
: __( 'Create new', 'woocommerce' ) }
</Button>
) }
>
{ items.map( ( child, index ) => (
<TreeItem
{ ...treeItemProps }
key={ child.data.value }
item={ child }
index={ index }
/>
) ) }
</ol>
</>
);
} );

View File

@ -14,7 +14,7 @@ export type CheckedStatus = 'checked' | 'unchecked' | 'indeterminate';
type BaseTreeProps = {
/**
* It contians one item if `multiple` value is false or
* It contains one item if `multiple` value is false or
* a list of items if it is true.
*/
selected?: Item | Item[];
@ -22,12 +22,30 @@ type BaseTreeProps = {
* Whether the tree items are single or multiple selected.
*/
multiple?: boolean;
/**
* In `multiple` mode, when this flag is also set, selecting children does
* not select their parents and selecting parents does not select their children.
*/
shouldNotRecursivelySelect?: boolean;
/**
* The value to be used for comparison to determine if 'create new' button should be shown.
*/
createValue?: string;
/**
* Called when the 'create new' button is clicked.
*/
onCreateNew?: () => void;
/**
* If passed, shows create button if return from callback is true
*/
shouldShowCreateButton?( value?: string ): boolean;
isExpanded?: 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
* @param value The selection
*/
onSelect?( value: Item | Item[] ): void;
/**
@ -36,7 +54,7 @@ type BaseTreeProps = {
* are also unselected. If it's false only the clicked item is
* unselected.
*
* @param value The unselection
* @param value The unselection
*/
onRemove?( value: Item | Item[] ): void;
/**
@ -48,12 +66,16 @@ type BaseTreeProps = {
* shouldItemBeHighlighted={ isFirstChild }
* />
*
* @param item The current linked tree item, useful to
* traverse the entire linked tree from this item.
* @param item The current linked tree item, useful to
* traverse the entire linked tree from this item.
*
* @see {@link LinkedTree}
*/
shouldItemBeHighlighted?( item: LinkedTree ): boolean;
/**
* Called when the create button is clicked to help closing any related popover.
*/
onTreeBlur?(): void;
};
export type TreeProps = BaseTreeProps &
@ -66,7 +88,8 @@ export type TreeProps = BaseTreeProps &
> & {
level?: number;
items: LinkedTree[];
/** It gives a way to render a different Element as the
/**
* It gives a way to render a different Element as the
* tree item label.
*
* @example
@ -74,7 +97,7 @@ export type TreeProps = BaseTreeProps &
* getItemLabel={ ( item ) => <span>${ item.data.label }</span> }
* />
*
* @param item The current rendering tree item
* @param item The current rendering tree item
*
* @see {@link LinkedTree}
*/
@ -107,8 +130,10 @@ export type TreeItemProps = BaseTreeProps &
level: number;
item: LinkedTree;
index: number;
isFocused?: boolean;
getLabel?( item: LinkedTree ): JSX.Element;
shouldItemBeExpanded?( item: LinkedTree ): boolean;
onLastItemLoop?( event: React.KeyboardEvent< HTMLDivElement > ): void;
};
export type TreeControlProps = Omit< TreeProps, 'items' | 'level' > & {

View File

@ -18,7 +18,7 @@ export type FormErrors< Values > = {
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type FormContext< Values extends Record< string, any > > = {
export type FormContextType< Values extends Record< string, any > > = {
values: Values;
errors: FormErrors< Values >;
isDirty: boolean;
@ -51,14 +51,16 @@ export type FormContext< Values extends Record< string, any > > = {
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const FormContext = createContext< FormContext< any > >(
export const FormContext: React.Context< FormContextType< any > > =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
{} as FormContext< any >
);
createContext< FormContextType< any > >(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
{} as FormContextType< any >
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function useFormContext< Values extends Record< string, any > >() {
const formContext = useContext< FormContext< Values > >( FormContext );
const formContext = useContext< FormContextType< Values > >( FormContext );
return formContext;
}

View File

@ -10,7 +10,7 @@ import { TextControl } from '@wordpress/components';
* Internal dependencies
*/
import { Form, useFormContext } from '../';
import type { FormContext } from '../';
import type { FormContextType } from '../';
import { DateTimePickerControl } from '../../date-time-picker-control';
const TestInputWithContext = () => {
@ -44,7 +44,7 @@ describe( 'Form', () => {
>
{ ( {
handleSubmit,
}: FormContext< Record< string, string > > ) => {
}: FormContextType< Record< string, string > > ) => {
return <button onClick={ handleSubmit }>Submit</button>;
} }
</Form>
@ -71,7 +71,7 @@ describe( 'Form', () => {
onChange={ mockOnChange }
validate={ () => ( {} ) }
>
{ ( { setValue }: FormContext< Record< string, string > > ) => {
{ ( { setValue }: FormContextType< Record< string, string > > ) => {
return (
<button
onClick={ () => {
@ -103,7 +103,7 @@ describe( 'Form', () => {
<Form onSubmit={ mockOnSubmit } validate={ () => ( {} ) }>
{ ( {
handleSubmit,
}: FormContext< Record< string, string > > ) => {
}: FormContextType< Record< string, string > > ) => {
return <button onClick={ handleSubmit }>Submit</button>;
} }
</Form>
@ -124,7 +124,7 @@ describe( 'Form', () => {
const { queryByText } = render(
<Form onChange={ mockOnChange } validate={ () => ( {} ) }>
{ ( { setValue }: FormContext< Record< string, string > > ) => {
{ ( { setValue }: FormContextType< Record< string, string > > ) => {
return (
<button
onClick={ () => {
@ -156,7 +156,7 @@ describe( 'Form', () => {
{ ( {
setValue,
getInputProps,
}: FormContext< Record< string, string > > ) => {
}: FormContextType< Record< string, string > > ) => {
return (
<TextControl
label={ 'First Name' }
@ -206,7 +206,7 @@ describe( 'Form', () => {
{ ( {
setValue,
getInputProps,
}: FormContext< Record< string, string > > ) => {
}: FormContextType< Record< string, string > > ) => {
return (
<TextControl
label={ 'First Name' }
@ -243,7 +243,7 @@ describe( 'Form', () => {
{ ( {
setValue,
getInputProps,
}: FormContext< Record< string, string > > ) => {
}: FormContextType< Record< string, string > > ) => {
return (
<TextControl
label={ 'First Name' }
@ -293,7 +293,7 @@ describe( 'Form', () => {
{ ( {
setValue,
getInputProps,
}: FormContext< Record< string, string > > ) => {
}: FormContextType< Record< string, string > > ) => {
return (
<TextControl
label={ 'First Name' }
@ -329,7 +329,7 @@ describe( 'Form', () => {
<Form onChanges={ mockOnChanges } validate={ () => ( {} ) }>
{ ( {
setValues,
}: FormContext< Record< string, string > > ) => {
}: FormContextType< Record< string, string > > ) => {
return (
<button
onClick={ () => {
@ -379,7 +379,7 @@ describe( 'Form', () => {
<Form onChanges={ mockOnChanges } validate={ validate }>
{ ( {
setValues,
}: FormContext< Record< string, string > > ) => {
}: FormContextType< Record< string, string > > ) => {
return (
<button
onClick={ () => {
@ -428,7 +428,7 @@ describe( 'Form', () => {
const { container, queryByText } = render(
<Form< TestData > onChange={ mockOnChange } validate={ validate }>
{ ( { getInputProps, values }: FormContext< TestData > ) => {
{ ( { getInputProps, values }: FormContextType< TestData > ) => {
return (
<DateTimePickerControl
label={ 'Date' }

View File

@ -16,7 +16,7 @@ export { default as EmptyContent } from './empty-content';
export { default as Flag } from './flag';
export { Form, useFormContext } from './form';
export { FormSection } from './form-section';
export type { FormContext, FormRef, FormErrors } from './form';
export type { FormContext, FormContextType, FormRef, FormErrors } from './form';
export { default as FilterPicker } from './filter-picker';
export { H, Section } from './section';
export { ImageGallery, ImageGalleryItem } from './image-gallery';
@ -95,7 +95,11 @@ export {
SlotContextType,
SlotContextHelpersType,
} from './slot-context';
export { TreeControl as __experimentalTreeControl } from './experimental-tree-control';
export {
TreeControl as __experimentalTreeControl,
Item as TreeItemType,
} from './experimental-tree-control';
export { SelectTree as __experimentalSelectTreeControl } from './experimental-select-tree-control';
export { default as TreeSelectControl } from './tree-select-control';
// Exports below can be removed once the @woocommerce/product-editor package is released.
@ -103,3 +107,4 @@ export {
ProductSectionLayout as __experimentalProductSectionLayout,
ProductFieldSection as __experimentalProductFieldSection,
} from './product-section-layout';
export { DisplayState } from './display-state';

View File

@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { BlockInstance } from '@wordpress/blocks';
import { BlockInstance, getBlockType } from '@wordpress/blocks';
import {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore We need this to import the block modules for registration.
@ -29,9 +29,10 @@ const ALLOWED_CORE_BLOCKS = [
const registerCoreBlocks = () => {
const coreBlocks = __experimentalGetCoreBlocks();
const blocks = coreBlocks.filter( ( block: BlockInstance ) =>
ALLOWED_CORE_BLOCKS.includes( block.name )
);
const blocks = coreBlocks.filter( ( block: BlockInstance ) => {
const isRegistered = !! getBlockType( block.name );
return ! isRegistered && ALLOWED_CORE_BLOCKS.includes( block.name );
} );
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore An argument is allowed to specify which blocks to register.

View File

@ -8,6 +8,6 @@ import { createContext } from '@wordpress/element';
*
* See https://medium.com/@Heydon/managing-heading-levels-in-design-systems-18be9a746fa3
*/
const Level = createContext( 2 );
const Level: React.Context< number > = createContext( 2 );
export { Level };

View File

@ -43,7 +43,7 @@ export type SortableProps = {
const THROTTLE_TIME = 16;
export const SortableContext = createContext( {} );
export const SortableContext: React.Context< object > = createContext( {} );
export const Sortable = ( {
children,

View File

@ -56,5 +56,6 @@
@import 'collapsible-content/style.scss';
@import 'form/style.scss';
@import 'experimental-tree-control/tree.scss';
@import 'experimental-select-tree-control/select-tree.scss';
@import 'product-section-layout/style.scss';
@import 'tree-select-control/index.scss';

View File

@ -28,13 +28,15 @@ function Tour() {
referenceElements: {
desktop: '.render-step-near-me',
},
meta: {
heading: 'Lorem ipsum dolor sit amet.',
descriptions: {
desktop: 'Lorem ipsum dolor sit amet.',
},
primaryButtonText: "Done"
},
meta: {
heading: 'Lorem ipsum dolor sit amet.',
descriptions: {
desktop: 'Lorem ipsum dolor sit amet.',
},
primaryButton: {
text: 'Done',
},
},
},
],
closeHandler: () => setShowTour( false ),
@ -57,8 +59,8 @@ function Tour() {
When a tour is rendered and focused, the following functionality exists:
- Close the tour on `ESC` key (in minimized view)
- Go to previous/next step on `left/right` arrow keys (in step view)
- Close the tour on `ESC` key (in minimized view)
- Go to previous/next step on `left/right` arrow keys (in step view)
## Configuration
@ -66,51 +68,52 @@ The main API for configuring a tour is the config object. See example usage and
`config.steps`: An array of objects that define the content we wish to render on the page. Each step defined by:
- `referenceElements` (optional): A set of `desktop` & `mobile` selectors to render the step near.
- `focusElement` (optional): A set of `desktop` & `mobile` & `iframe` selectors to automatically focus.
- `meta`: Arbitrary object that encloses the content we want to render for each step.
- `classNames` (optional): An array or CSV of CSS classes applied to a step.
- `referenceElements` (optional): A set of `desktop` & `mobile` selectors to render the step near.
- `focusElement` (optional): A set of `desktop` & `mobile` & `iframe` selectors to automatically focus.
- `meta`: Arbitrary object that encloses the content we want to render for each step.
- `classNames` (optional): An array or CSV of CSS classes applied to a step.
`config.closeHandler`: The callback responsible for closing the tour.
- `tourStep`: A React component that will be called to render each step. Receives the following properties:
- `tourStep`: A React component that will be called to render each step. Receives the following properties:
- `steps`: The steps defined for the tour.
- `currentStepIndex`
- `onDismiss`: Handler that dismissed/closes the tour.
- `onNext`: Handler that progresses the tour to the next step.
- `onPrevious`: Handler that takes the tour to the previous step.
- `onMinimize`: Handler that minimizes the tour (passes rendering to `tourMinimized`).
- `setInitialFocusedElement`: A dispatcher that assigns an element to be initially focused when a step renders (see examples).
- `onGoToStep`: Handler that progresses the tour to a given step index.
- `steps`: The steps defined for the tour.
- `currentStepIndex`
- `onDismiss`: Handler that dismissed/closes the tour.
- `onNext`: Handler that progresses the tour to the next step.
- `onPrevious`: Handler that takes the tour to the previous step.
- `onMinimize`: Handler that minimizes the tour (passes rendering to `tourMinimized`).
- `setInitialFocusedElement`: A dispatcher that assigns an element to be initially focused when a step renders (see examples).
- `onGoToStep`: Handler that progresses the tour to a given step index.
- `tourMinimized`: A React component that will be called to render a minimized view for the tour. Receives the following properties:
- `steps`: The steps defined for the tour.
- `currentStepIndex`
- `onDismiss`: Handler that dismissed/closes the tour.
- `onMaximize`: Handler that expands the tour (passes rendering to `tourStep`).
- `tourMinimized`: A React component that will be called to render a minimized view for the tour. Receives the following properties:
- `steps`: The steps defined for the tour.
- `currentStepIndex`
- `onDismiss`: Handler that dismissed/closes the tour.
- `onMaximize`: Handler that expands the tour (passes rendering to `tourStep`).
`config.options` (optional):
- `classNames` (optional): An array or CSV of CSS classes to enclose the main tour frame with.
- `classNames` (optional): An array or CSV of CSS classes to enclose the main tour frame with.
- `effects`: An object to enable/disable/combine various tour effects:
- `effects`: An object to enable/disable/combine various tour effects:
- `spotlight`: Adds a semi-transparent overlay and highlights the reference element when provided with a transparent box over it. Expects an object with optional styles to override the default highlight/spotlight behavior when provided (default: spotlight wraps the entire reference element).
- `interactivity`: An object that configures whether the user is allowed to interact with the referenced element during the tour
- `styles`: CSS properties that configures the styles applied to the spotlight overlay
- `arrowIndicator`: Adds an arrow tip pointing at the reference element when provided.
- `overlay`: Includes the semi-transparent overlay for all the steps (also blocks interactions with the rest of the page)
- `autoScroll`: The page scrolls up and down automatically such that the step target element is visible to the user.
- `spotlight`: Adds a semi-transparent overlay and highlights the reference element when provided with a transparent box over it. Expects an object with optional styles to override the default highlight/spotlight behavior when provided (default: spotlight wraps the entire reference element).
- `interactivity`: An object that configures whether the user is allowed to interact with the referenced element during the tour
- `styles`: CSS properties that configures the styles applied to the spotlight overlay
- `arrowIndicator`: Adds an arrow tip pointing at the reference element when provided.
- `overlay`: Includes the semi-transparent overlay for all the steps (also blocks interactions with the rest of the page)
- `autoScroll`: The page scrolls up and down automatically such that the step target element is visible to the user.
- `callbacks`: An object of callbacks to handle side effects from various interactions (see [types.ts](./src/types.ts)).
- `callbacks`: An object of callbacks to handle side effects from various interactions (see [types.ts](./src/types.ts)).
- `popperModifiers`: The tour uses Popper to position steps near reference elements (and for other effects). An implementation can pass its own modifiers to tailor the functionality further e.g. more offset or padding from the reference element.
- `tourRating` (optional - only in WPCOM Tour Kit variant):
- `enabled`: Whether to show rating in last step.
- `useTourRating`: (optional) A hook to provide the rating from an external source/state (see [types.ts](./src/types.ts)).
- `onTourRate`: (optional) A callback to fire off when a rating is submitted.
- `popperModifiers`: The tour uses Popper to position steps near reference elements (and for other effects). An implementation can pass its own modifiers to tailor the functionality further e.g. more offset or padding from the reference element.
- `tourRating` (optional - only in WPCOM Tour Kit variant):
- `portalElementId`: A string that lets you customize under which DOM element the Tour will be appended.
- `enabled`: Whether to show rating in last step.
- `useTourRating`: (optional) A hook to provide the rating from an external source/state (see [types.ts](./src/types.ts)).
- `onTourRate`: (optional) A callback to fire off when a rating is submitted.
- `portalElementId`: A string that lets you customize under which DOM element the Tour will be appended.
`placement` (Optional) : Describes the preferred placement of the popper. Possible values are left-start, left, left-end, top-start, top, top-end, right-start, right, right-end, bottom-start, bottom, and bottom-end.

View File

@ -15,19 +15,19 @@ import { BACKSPACE } from './constants';
* The Control Component renders a search input and also the Tags.
* It also triggers the setExpand for expanding the options tree on click.
*
* @param {Object} props Component props
* @param {Array} props.tags Array of tags
* @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
* @param {Function} props.onFocus On Focus Callback
* @param {Function} props.onTagsChange Callback when the Tags change
* @param {Function} props.onInputChange Callback when the Input value changes
* @param {Function} [props.onControlClick] Callback when clicking on the control.
* @param {Object} props Component props
* @param {Array} props.tags Array of tags
* @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
* @param {Function} props.onFocus On Focus Callback
* @param {Function} props.onTagsChange Callback when the Tags change
* @param {Function} props.onInputChange Callback when the Input value changes
* @param {Function} [props.onControlClick] Callback when clicking on the control.
* @return {JSX.Element} The rendered component
*/
const Control = forwardRef(

View File

@ -70,7 +70,7 @@ import { ARROW_DOWN, ARROW_UP, ENTER, ESCAPE, ROOT_VALUE } from './constants';
* @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
* @param {Function} [props.onInputChange] Callback when the selector changes
* @return {JSX.Element} The component
*/
const TreeSelectControl = ( {

View File

@ -64,7 +64,7 @@ function createOrderedChildren< T = Fill.Props, S = Record< string, unknown > >(
order: number,
props: T,
injectProps?: S
) {
): React.ReactElement {
const { children: childrenToRender, props: propsToRender } =
getChildrenAndProps( children, order, props, injectProps );
return cloneElement( childrenToRender, propsToRender );

View File

@ -1,6 +1,10 @@
{
"extends": "../tsconfig-cjs",
"compilerOptions": {
"outDir": "build"
"outDir": "build",
"typeRoots": [
"./typings",
"./node_modules/@types"
]
}
}

View File

@ -6,6 +6,9 @@
"declaration": true,
"declarationMap": true,
"declarationDir": "./build-types",
"composite": true
"typeRoots": [
"./typings",
"./node_modules/@types"
]
}
}

View File

@ -12,6 +12,7 @@ module.exports = {
path: __dirname,
},
module: {
parser: webpackConfig.parser,
rules: webpackConfig.rules,
},
plugins: webpackConfig.plugins,

View File

@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: TypeScript build change

View File

@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: Configuration change only

View File

@ -0,0 +1,5 @@
Significance: patch
Type: dev
Comment: Configuration change only

View File

@ -1,4 +1,4 @@
{
"rootDir": "./src",
"preset": "../../internal-js-tests/jest.config.js"
"preset": "../node_modules/@woocommerce/internal-js-tests/jest-preset.js"
}

View File

@ -26,6 +26,7 @@
"types": "build-types",
"react-native": "src/index",
"dependencies": {
"@types/node": "^16.18.18",
"browser-filesaver": "^1.1.1",
"moment": "^2.29.1"
},
@ -33,7 +34,7 @@
"access": "public"
},
"scripts": {
"turbo:build": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
"turbo:build": "tsc --project tsconfig.json && tsc --project tsconfig-cjs.json",
"turbo:test": "jest --config ./jest.config.json",
"prepare": "composer install",
"changelog": "composer exec -- changelogger",
@ -41,7 +42,7 @@
"build": "pnpm -w exec turbo run turbo:build --filter=$npm_package_name",
"test": "pnpm -w exec turbo run turbo:test --filter=$npm_package_name",
"lint": "eslint src",
"start": "tsc --build --watch",
"start": "concurrently \"tsc --project tsconfig.json --watch\" \"tsc --project tsconfig-cjs.json --watch\"",
"prepack": "pnpm run clean && pnpm run build",
"lint:fix": "eslint src --fix",
"test-staged": "jest --bail --config ./jest.config.json --findRelatedTests"
@ -50,12 +51,14 @@
"@babel/core": "^7.17.5",
"@types/jest": "^27.4.1",
"@woocommerce/eslint-plugin": "workspace:*",
"@woocommerce/internal-js-tests": "workspace:*",
"eslint": "^8.32.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"concurrently": "^7.0.0",
"rimraf": "^3.0.2",
"ts-jest": "^27.1.3",
"typescript": "^4.8.3"
"typescript": "^4.9.5"
},
"lint-staged": {
"*.(t|j)s?(x)": [

View File

@ -1,6 +1,10 @@
{
"extends": "../tsconfig-cjs",
"compilerOptions": {
"outDir": "build"
"outDir": "build",
"typeRoots": [
"./typings",
"./node_modules/@types"
]
}
}

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