diff --git a/.codecov.yml b/.codecov.yml index bec8da4c270..907ee344cb6 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,27 +1,46 @@ codecov: - notify: - require_ci_to_pass: yes + notify: + require_ci_to_pass: yes + +ignore: + - '**/tests' + - '**/test' + - 'tools/**' + - 'packages/js/admin-e2e-tests' + - 'packages/js/api-core-tests' + - 'packages/js/create-woo-extension' + - 'packages/js/e2e-core-tests' + - 'packages/js/e2e-environment' + - 'packages/js/e2e-utils' + - 'packages/js/eslint-plugin' + - 'packages/js/internal-e2e-builds' + - 'packages/js/internal-js-tests' + - 'packages/js/internal-style-build' + - '**/*.test.*' coverage: - precision: 2 - round: nearest - range: "50...100" - - status: - project: - default: - informational: true - patch: - default: - informational: true - changes: off + precision: 1 + round: nearest + range: '50...80' + status: + project: + default: + target: auto + patch: + default: + target: auto parsers: - gcov: - branch_detection: - conditional: yes - loop: yes - method: no - macro: no + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no -comment: false +comment: + layout: 'reach, diff, flags, files' + behavior: default + require_changes: false + require_base: no + require_head: yes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..7bc3d3c2e44 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +* text=auto + +# Force LF In Configuration Files +*.md text eol=lf +*.json text eol=lf +*.yml text eol=lf + +# Force LF In Code Files +*.php text eol=lf +*.js text eol=lf +*.jsx text eol=lf +*.ts text eol=lf +*.tsx text eol=lf +*.css text eol=lf +*.scss text eol=lf diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3e3b424196e..df2c01a88bf 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -7,11 +7,11 @@ There are many ways to contribute to the project! - [Translating strings into your language](https://github.com/woocommerce/woocommerce/wiki/Translating-WooCommerce). - Answering questions on the various WooCommerce communities like the [WP.org support forums](https://wordpress.org/support/plugin/woocommerce/). - Testing open [issues](https://github.com/woocommerce/woocommerce/issues) or [pull requests](https://github.com/woocommerce/woocommerce/pulls) and sharing your findings in a comment. -- Testing WooCommerce beta versions and release candidates. Those are announced in the [WooCommerce development blog](https://woocommerce.wordpress.com/). +- Testing WooCommerce beta versions and release candidates. Those are announced in the [WooCommerce development blog](https://developer.woocommerce.com/blog/). - Submitting fixes, improvements, and enhancements. - To disclose a security issue to our team, [please submit a report via HackerOne](https://hackerone.com/automattic/). -If you wish to contribute code, please read the information in the sections below. Then [fork](https://help.github.com/articles/fork-a-repo/) WooCommerce, commit your changes, and [submit a pull request](https://help.github.com/articles/using-pull-requests/) 🎉 +If you wish to contribute code, please read the information in the sections below. Then [fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) WooCommerce, commit your changes, and [submit a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) 🎉 We use the `good first issue` label to mark issues that are suitable for new contributors. You can find all the issues with this label [here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+good+first+issue%22). @@ -21,6 +21,8 @@ If you have questions about the process to contribute code or want to discuss de ## Getting started +Please take a moment to review the [project readme](https://github.com/woocommerce/woocommerce/blob/trunk/README.md) and our [development notes](https://github.com/woocommerce/woocommerce/blob/trunk/DEVELOPMENT.md), which cover the basics needed to start working on this project. You may also be interested in the following resources: + - [How to set up WooCommerce development environment](https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment) - [Git Flow](https://github.com/woocommerce/woocommerce/wiki/WooCommerce-Git-Flow) - [Minification of SCSS and JS](https://github.com/woocommerce/woocommerce/wiki/Minification-of-SCSS-and-JS) @@ -31,7 +33,7 @@ If you have questions about the process to contribute code or want to discuss de ## Coding Guidelines and Development 🛠 -- Ensure you stick to the [WordPress Coding Standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards/php/) +- Ensure you stick to the [WordPress Coding Standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards/php/). - Run our build process described in the document on [how to set up WooCommerce development environment](https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment), it will install our pre-commit hook, code sniffs, dependencies, and more. - Whenever possible please fix pre-existing code standards errors in the files that you change. It is ok to skip that for larger files or complex fixes. - Ensure you use LF line endings in your code editor. Use [EditorConfig](http://editorconfig.org/) if your editor supports it so that indentation, line endings and other settings are auto configured. @@ -39,16 +41,13 @@ 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= 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. - -- [Blocks](https://github.com/woocommerce/woocommerce-gutenberg-products-block) +If you are contributing code to our (Javascript-driven) Gutenberg blocks, please note that they are developed in their [own repository](https://github.com/woocommerce/woocommerce-gutenberg-products-block) and have their [own issue tracker](https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues). ## Feature Requests 🚀 -Feature requests can be [submitted to our issue tracker](https://github.com/woocommerce/woocommerce/issues/new?assignees=&labels=type%3A+enhancement%2Cstatus%3A+awaiting+triage&template=2-enhancement.yml&title=%5BEnhancement%5D%3A+). Be sure to include a description of the expected behavior and use case, and before submitting a request, please search for similar ones in the closed issues. +The best place to submit feature requests is over on our [dedicated feature request page](https://woocommerce.com/feature-requests/woocommerce/). You can easily search and vote for existing requests, or create new requests if necessary. -Feature request issues will remain closed until we see sufficient interest via comments and [👍 reactions](https://help.github.com/articles/about-discussions-in-issues-and-pull-requests/) from the community. - -You can see a [list of current feature requests which require votes here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+sort%3Areactions-%2B1-desc+label%3A%22needs%3A+votes%22+). +Alternatively, if you wish to propose a straightforward technical enhancement that is unlikely to require much discussion, you can [open a new issue](https://github.com/woocommerce/woocommerce/issues/new?assignees=&labels=type%3A+enhancement%2Cstatus%3A+awaiting+triage&template=2-enhancement.yml&title=%5BEnhancement%5D%3A+) right here on GitHub and, for any that may require more discussion, consider syncing with us during office hours or publishing a thread on [GitHub Discussions](https://github.com/woocommerce/woocommerce/discussions). diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7114392b602..0ff2173615b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -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? - - +- 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. @@ -14,30 +13,16 @@ Closes # . - - -- [ ] This PR is a very minor change/addition and does not require testing instructions (if checked you can ignore/remove the next section). - ### How to test the changes in this Pull Request: - + + +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. - - -### 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= changelog add`? - - - -### 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. + \ No newline at end of file diff --git a/.github/actions/setup-woocommerce-monorepo/action.yml b/.github/actions/setup-woocommerce-monorepo/action.yml index 3ca1431d678..323977de415 100644 --- a/.github/actions/setup-woocommerce-monorepo/action.yml +++ b/.github/actions/setup-woocommerce-monorepo/action.yml @@ -1,70 +1,84 @@ name: Setup WooCommerce Monorepo description: Handles the installation, building, and caching of the projects within the monorepo. +permissions: {} inputs: - install-filters: - description: The PNPM filter used to decide what projects to install. Supports multiline strings for multiple filters. - default: "" - build: - description: Indicates whether or not the action should build any projects. - default: "true" - build-filters: - description: The PNPM filter used to decide what projects to build. Supports multiline strings for multiple filters. - default: "" - php-version: - description: The version of PHP that the action should set up. - default: "7.4" + install-filters: + description: The PNPM filter used to decide what projects to install. Supports multiline strings for multiple filters. + default: '' + build: + description: Indicates whether or not the action should build any projects. + default: 'true' + build-filters: + description: The PNPM filter used to decide what projects to build. Supports multiline strings for multiple filters. + default: '' + php-version: + description: The version of PHP that the action should set up. + default: '7.4' runs: - using: composite - steps: - - name: Parse Action Input - id: parse-input - shell: bash - run: | - echo "::set-output name=INSTALL_FILTERS::$(node ./.github/actions/setup-woocommerce-monorepo/scripts/parse-input-filter.js '${{ inputs.install-filters }}')" - echo "::set-output name=BUILD_FILTERS::$(node ./.github/actions/setup-woocommerce-monorepo/scripts/parse-input-filter.js '${{ inputs.build-filters }}')" + using: composite + steps: + - name: Parse Action Input + id: parse-input + shell: bash + run: | + echo "INSTALL_FILTERS=$(node ./.github/actions/setup-woocommerce-monorepo/scripts/parse-input-filter.js '${{ inputs.install-filters }}')" >> $GITHUB_OUTPUT + echo "BUILD_FILTERS=$(node ./.github/actions/setup-woocommerce-monorepo/scripts/parse-input-filter.js '${{ inputs.build-filters }}')" >> $GITHUB_OUTPUT - - name: Setup PNPM - uses: pnpm/action-setup@10693b3829bf86eb2572aef5f3571dcf5ca9287d - with: - version: "^7.13.3" + - name: Setup PNPM + uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd + with: + version: '8.3.1' - - name: Setup Node - uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 - with: - node-version-file: .nvmrc - cache: pnpm - registry-url: 'https://registry.npmjs.org' - - - name: Setup PHP - uses: shivammathur/setup-php@e04e1d97f0c0481c6e1ba40f8a538454fe5d7709 - with: - php-version: ${{ inputs.php-version }} - coverage: none - tools: cs2pr, phpcs + - name: Setup Node + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c + with: + node-version-file: .nvmrc + cache: pnpm + registry-url: 'https://registry.npmjs.org' - - name: Cache Composer Dependencies - uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 - with: - path: ~/.cache/composer/files - key: ${{ runner.os }}-php-${{ inputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ runner.os }}-php-${{ inputs.php-version }}-composer- + - name: Setup PHP + uses: shivammathur/setup-php@8e2ac35f639d3e794c1da1f28999385ab6fdf0fc + with: + php-version: ${{ inputs.php-version }} + coverage: none + tools: phpcs, sirbrillig/phpcs-changed - - name: Install Node and PHP Dependencies - shell: bash - run: | - pnpm -w install turbo - pnpm install ${{ steps.parse-input.outputs.INSTALL_FILTERS }} + - name: Cache Composer Dependencies + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 + with: + path: ~/.cache/composer/files + key: ${{ runner.os }}-php-${{ inputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-php-${{ inputs.php-version }}-composer- - - name: Cache Build Output - uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 - with: - path: node_modules/.cache/turbo - key: ${{ runner.os }}-build-output-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }} - restore-keys: ${{ runner.os }}-build-output- + - name: Install Node and PHP Dependencies + shell: bash + run: | + pnpm -w install turbo + pnpm install ${{ steps.parse-input.outputs.INSTALL_FILTERS }} - - name: Build - if: ${{ inputs.build == 'true' }} - shell: bash - run: pnpm -w exec turbo run turbo:build ${{ steps.parse-input.outputs.BUILD_FILTERS }} + - name: Get branch name + id: get_branch + shell: bash + run: | + if [ "${{ github.event_name }}" == "pull_request" ]; then + branch_name=$(echo "${{ github.head_ref }}" | tr '/' '-') + echo "CURRENT_BRANCH_NAME=$branch_name" >> $GITHUB_OUTPUT + else + echo "CURRENT_BRANCH_NAME=${{ github.ref_name }}" >> $GITHUB_OUTPUT + fi + + - name: Cache Build Output + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 + with: + path: .turbo + key: ${{ runner.os }}-build-output-${{ steps.get_branch.outputs.CURRENT_BRANCH_NAME }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-build-output-${{ steps.get_branch.outputs.CURRENT_BRANCH_NAME }} + ${{ runner.os }}-build-output + + - name: Build + if: ${{ inputs.build == 'true' }} + shell: bash + run: pnpm -w exec turbo run turbo:build --cache-dir=".turbo" ${{ steps.parse-input.outputs.BUILD_FILTERS }} diff --git a/.github/actions/tests/run-api-tests/action.yml b/.github/actions/tests/run-api-tests/action.yml new file mode 100644 index 00000000000..598a67a0d5f --- /dev/null +++ b/.github/actions/tests/run-api-tests/action.yml @@ -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 diff --git a/.github/actions/tests/run-e2e-tests/action.yml b/.github/actions/tests/run-e2e-tests/action.yml new file mode 100644 index 00000000000..ab04b6764c4 --- /dev/null +++ b/.github/actions/tests/run-e2e-tests/action.yml @@ -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 diff --git a/.github/actions/tests/run-k6-tests/action.yml b/.github/actions/tests/run-k6-tests/action.yml new file mode 100644 index 00000000000..fbf67c7f9f6 --- /dev/null +++ b/.github/actions/tests/run-k6-tests/action.yml @@ -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 diff --git a/.github/actions/tests/setup-local-test-environment/action.yml b/.github/actions/tests/setup-local-test-environment/action.yml new file mode 100644 index 00000000000..c5386a32c77 --- /dev/null +++ b/.github/actions/tests/setup-local-test-environment/action.yml @@ -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 diff --git a/.github/actions/tests/slack-alert-on-pr-merge/action.yml b/.github/actions/tests/slack-alert-on-pr-merge/action.yml new file mode 100644 index 00000000000..6fbc11ab62a --- /dev/null +++ b/.github/actions/tests/slack-alert-on-pr-merge/action.yml @@ -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 }} diff --git a/.github/actions/tests/slack-alert-on-pr-merge/scripts/compose-slack-message.js b/.github/actions/tests/slack-alert-on-pr-merge/scripts/compose-slack-message.js new file mode 100644 index 00000000000..ca194507547 --- /dev/null +++ b/.github/actions/tests/slack-alert-on-pr-merge/scripts/compose-slack-message.js @@ -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 `, + }, + }; + 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 }; +}; diff --git a/.github/actions/tests/upload-allure-files-to-bucket/action.yml b/.github/actions/tests/upload-allure-files-to-bucket/action.yml new file mode 100644 index 00000000000..c99f5fd5c42 --- /dev/null +++ b/.github/actions/tests/upload-allure-files-to-bucket/action.yml @@ -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 diff --git a/.github/actions/tests/upload-allure-files-to-bucket/scripts/upload-allure-artifact.sh b/.github/actions/tests/upload-allure-files-to-bucket/scripts/upload-allure-artifact.sh new file mode 100644 index 00000000000..bc3a2cd810b --- /dev/null +++ b/.github/actions/tests/upload-allure-files-to-bucket/scripts/upload-allure-artifact.sh @@ -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 \ No newline at end of file diff --git a/.github/project-pr-labeler.yml b/.github/project-pr-labeler.yml index 9f95140db32..c80da44a922 100644 --- a/.github/project-pr-labeler.yml +++ b/.github/project-pr-labeler.yml @@ -61,7 +61,16 @@ 'plugin: woocommerce': - plugins/woocommerce/**/* -'focus: react admin': +'focus: react admin [team:Ghidorah]': - 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/**/* diff --git a/.github/workflows/build-release-zip-file.yml b/.github/workflows/build-release-zip-file.yml index c1cc55eb04d..f07d234fc40 100644 --- a/.github/workflows/build-release-zip-file.yml +++ b/.github/workflows/build-release-zip-file.yml @@ -6,10 +6,15 @@ on: description: 'By default the zip file is generated from the branch the workflow runs from, but you can specify an explicit reference to use instead here (e.g. refs/tags/tag_name or refs/heads/release/x.x). The resulting file will be available as an artifact on the workflow run.' required: false default: '' + +permissions: {} + jobs: build: name: Build release zip file runs-on: ubuntu-20.04 + permissions: + contents: read steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index ba2b99d4814..5c02d361b52 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -2,10 +2,15 @@ name: Build release asset on: release: types: [published] + +permissions: {} + jobs: build: name: Build release asset runs-on: ubuntu-20.04 + permissions: + contents: write steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/bump-wp-l-2-support.yml b/.github/workflows/bump-wp-l-2-support.yml new file mode 100644 index 00000000000..71ea90ad73e --- /dev/null +++ b/.github/workflows/bump-wp-l-2-support.yml @@ -0,0 +1,287 @@ +name: Bump WP L-2 Support +on: + workflow_dispatch: + inputs: + releaseBranch: + description: Provide the release branch you want to bump the WP L-2 support. Example release/6.9. Note that trunk will also be bumped to match. + default: '' + required: true + +permissions: {} + +env: + GIT_COMMITTER_NAME: 'WooCommerce Bot' + GIT_COMMITTER_EMAIL: 'no-reply@woocommerce.com' + GIT_AUTHOR_NAME: 'WooCommerce Bot' + GIT_AUTHOR_EMAIL: 'no-reply@woocommerce.com' + +jobs: + check-release-branch-exists: + name: Check for existence of release branch + runs-on: ubuntu-20.04 + steps: + - name: Check for release branch + id: release-branch-check + uses: actions/github-script@v6 + with: + script: | + // This will throw an error for non-200 responses, which prevents subsequent jobs from completing, as desired. + await github.request( 'GET /repos/{owner}/{repo}/branches/{branch}', { + owner: context.repo.owner, + repo: context.repo.repo, + branch: '${{ inputs.releaseBranch }}', + } ); + + validate-bump: + name: Validate and bump WP L-2 support version + runs-on: ubuntu-20.04 + needs: check-release-branch-exists + if: success() + permissions: + actions: write + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v3 + + - name: Get latest WP version + id: latestWP + uses: actions/github-script@v6 + with: + script: | + const https = require( 'https' ); + + https.get( 'https://api.wordpress.org/core/stable-check/1.0/', ( resp ) => { + let data = ''; + + // A chunk of data has been received. + resp.on( 'data', ( chunk ) => { + data += chunk; + } ); + + // The whole response has been received. Print out the result. + resp.on( 'end', () => { + JSON.parse(data, ( key, val ) => { + if ( val === 'latest' ) { + core.setOutput( 'version', key ) + } + } ); + } ); + + } ).on( 'error', ( err ) => { + console.log( 'Error: ' + err.message ); + } ); + + - name: Get L-2 WP version + id: l2Version + if: steps.latestWP.outputs.version != '' && steps.latestWP.outputs.version != null + uses: actions/github-script@v6 + with: + script: | + const version = "${{ steps.latestWP.outputs.version }}"; + const latestWPVersionMajor = version.split( '.' )[0]; + const latestWPVersionMinor = version.split( '.' )[1]; + const l2 = (parseInt( ( latestWPVersionMajor + latestWPVersionMinor ), 10 ) - 2 ).toString(); + const l2Major = l2.split( '' )[0]; + const l2Minor = l2.split( '' )[1]; + core.setOutput( 'version', l2Major + '.' + l2Minor ); + + - name: Git fetch the release branch + run: git fetch origin ${{ inputs.releaseBranch }} + + - name: Checkout release branch + run: git checkout ${{ inputs.releaseBranch }} + + - name: Create a PR branch based on release branch + run: git checkout -b WP-L-2-version-support-${{ steps.l2Version.outputs.version }}/${{ inputs.releaseBranch }} + + - name: Check if WP L-2 support needs to be bumped the release branch + id: readmeWPVersion + if: steps.l2Version.outputs.version != '' && steps.l2Version.outputs.version != null + uses: actions/github-script@v6 + with: + script: | + const fs = require( 'node:fs' ); + const l2Version = "${{ steps.l2Version.outputs.version }}"; + let readme = ''; + + fs.readFile( './plugins/woocommerce/readme.txt', 'utf-8', function( err, data ) { + if ( err ) { + console.error( err ); + } + + readme = data.split( "\n" ); + const newReadme = []; + let needsChange = false; + + for ( const line of readme ) { + if ( line.match( /Requires\sat\sleast:\s\d+\.\d/ ) ) { + const readmeVersion = line.match( /\d+\.\d/ ); + + // If the versions don't match, means we need to make a change. + if ( readmeVersion != l2Version ) { + needsChange = true; + + newReadme.push( 'Requires at least: ' + l2Version ); + continue; + } + } + + newReadme.push( line ); + } + + if ( needsChange ) { + fs.writeFile( './plugins/woocommerce/readme.txt', newReadme.join( "\n" ), err => { + if ( err ) { + core.setFailed( `Unable to bump the WP L-2 support version. ${err}` ); + } + + core.setOutput( 'needsChange', needsChange ); + + // Copy the readme.txt file to the root of VM to be used later. + fs.writeFile( '../../readme.txt', newReadme.join( "\n" ), err => { + if ( err ) { + core.setFailed( `Unable to copy the readme.txt file to the root of VM. ${err}` ); + } + } ); + } ); + } else { + core.setFailed( 'No changes needed. WP Version is L-2 compatible.' ); + } + } ); + + - name: Commit changes + if: steps.readmeWPVersion.outputs.needsChange == 'true' + run: git commit --no-verify -am "Update readme.txt WP L-2 support version." + + - name: Push changes + if: steps.readmeWPVersion.outputs.needsChange == 'true' + run: git push origin WP-L-2-version-support-${{ steps.l2Version.outputs.version }}/${{ inputs.releaseBranch }} + + - name: Push the PR up to GitHub + id: release-branch-pr + if: steps.readmeWPVersion.outputs.needsChange == 'true' + uses: actions/github-script@v6 + with: + script: | + const PRBody = "This PR bumps the WP version to L-2 compatible for the release branch ${{ inputs.releaseBranch }}.\n"; + + const pr = await github.rest.pulls.create( { + owner: context.repo.owner, + repo: context.repo.repo, + title: "Bump WP Version to L-2 compatible for ${{ inputs.releaseBranch }}", + head: "WP-L-2-version-support-${{ steps.l2Version.outputs.version }}/${{ inputs.releaseBranch }}", + base: "${{ inputs.releaseBranch }}", + body: PRBody + } ); + + if ( pr.status != 201 ) { + core.setFailed( "Unable to push WP-L-2-version-support-${{ steps.l2Version.outputs.version }}/${{ inputs.releaseBranch }} to GitHub." ); + } + + core.setOutput( 'pr', pr.data.number ); + + await github.rest.pulls.requestReviewers( { + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.data.number, + reviewers: [ context.actor ] + } ); + + - name: Checkout trunk branch + if: steps.release-branch-pr.outputs.pr != '' && steps.release-branch-pr.outputs.pr != null + run: git checkout trunk + + - name: Create a branch based on trunk branch + if: steps.release-branch-pr.outputs.pr != '' && steps.release-branch-pr.outputs.pr != null + run: git checkout -b WP-L-2-version-support-${{ steps.l2Version.outputs.version }}/trunk + + - name: Check if WP L-2 support needs to be bumped for trunk + id: readmeWPVersionTrunk + if: steps.release-branch-pr.outputs.pr != '' && steps.release-branch-pr.outputs.pr != null + uses: actions/github-script@v6 + with: + script: | + const fs = require( 'node:fs' ); + const l2Version = "${{ steps.l2Version.outputs.version }}"; + let readme = ''; + + fs.readFile( './plugins/woocommerce/readme.txt', 'utf-8', function( err, data ) { + if ( err ) { + console.error( err ); + } + + readme = data.split( "\n" ); + const newReadme = []; + let needsChange = false; + + for ( const line of readme ) { + if ( line.match( /Requires\sat\sleast:\s\d+\.\d/ ) ) { + const readmeVersion = line.match( /\d+\.\d/ ); + + // If the versions don't match, means we need to make a change. + if ( readmeVersion != l2Version ) { + needsChange = true; + + newReadme.push( 'Requires at least: ' + l2Version ); + continue; + } + } + + newReadme.push( line ); + } + + if ( needsChange ) { + fs.writeFile( './plugins/woocommerce/readme.txt', newReadme.join( "\n" ), err => { + if ( err ) { + core.setFailed( `Unable to bump the WP L-2 support version. ${err}` ); + } + + core.setOutput( 'needsChange', needsChange ); + + // Copy the readme.txt file to the root of VM to be used later. + fs.writeFile( '../../readme.txt', newReadme.join( "\n" ), err => { + if ( err ) { + core.setFailed( `Unable to copy the readme.txt file to the root of VM. ${err}` ); + } + } ); + } ); + } else { + core.setFailed( 'No changes needed. WP Version is L-2 compatible.' ); + } + } ); + + - name: Commit changes + if: steps.readmeWPVersionTrunk.outputs.needsChange == 'true' + run: git commit --no-verify -am "Update readme.txt WP L-2 support version." + + - name: Push changes + if: steps.readmeWPVersionTrunk.outputs.needsChange == 'true' + run: git push origin WP-L-2-version-support-${{ steps.l2Version.outputs.version }}/trunk + + - name: Push the PR up to GitHub + if: steps.readmeWPVersionTrunk.outputs.needsChange == 'true' + uses: actions/github-script@v6 + with: + script: | + const PRBody = "This PR bumps the WP version to L-2 compatible for trunk.\n"; + + const pr = await github.rest.pulls.create( { + owner: context.repo.owner, + repo: context.repo.repo, + title: "Bump WP Version to L-2 compatible for trunk", + head: "WP-L-2-version-support-${{ steps.l2Version.outputs.version }}/trunk", + base: "trunk", + body: PRBody + } ); + + if ( pr.status != 201 ) { + core.setFailed( "Unable to push WP-L-2-version-support-${{ steps.l2Version.outputs.version }}/trunk to GitHub." ); + } + + await github.rest.pulls.requestReviewers( { + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.data.number, + reviewers: [ context.actor ] + } ); diff --git a/.github/workflows/cherry-pick.yml b/.github/workflows/cherry-pick.yml index 81071f1e5ef..280a3268b57 100644 --- a/.github/workflows/cherry-pick.yml +++ b/.github/workflows/cherry-pick.yml @@ -1,28 +1,28 @@ name: Cherry Pick Tool on: - issues: - types: [milestoned, labeled] - pull_request: - types: [closed] - workflow_dispatch: - inputs: - release_branch: - description: Provide the release branch you want to cherry pick into. Example release/6.9 - default: '' - required: true - pull_requests: - description: The pull request number. - default: '' - required: true - skipSlackPing: - description: "Skip Slack Ping: If true, the Slack ping will be skipped (useful for testing)" - type: boolean - required: false - default: false - slackChannelOverride: - description: "Slack Channel Override: The channel ID to send the Slack ping about the code freeze violation" - required: false - default: '' + issues: + types: [milestoned, labeled] + pull_request: + types: [closed] + workflow_dispatch: + inputs: + release_branch: + description: Provide the release branch you want to cherry pick into. Example release/6.9 + default: '' + required: true + pull_requests: + description: The pull request number. + default: '' + required: true + skipSlackPing: + description: 'Skip Slack Ping: If true, the Slack ping will be skipped (useful for testing)' + type: boolean + required: false + default: false + slackChannelOverride: + description: 'Slack Channel Override: The channel ID to send the Slack ping about the code freeze violation' + required: false + default: '' env: GIT_COMMITTER_NAME: 'WooCommerce Bot' @@ -30,321 +30,328 @@ env: GIT_AUTHOR_NAME: 'WooCommerce Bot' GIT_AUTHOR_EMAIL: 'no-reply@woocommerce.com' +permissions: {} + jobs: - verify: - name: Verify - runs-on: ubuntu-20.04 - outputs: - run: ${{ steps.check.outputs.run }} - steps: - - name: check - id: check - uses: actions/github-script@v6 - with: - script: | - let run = false; + verify: + name: Verify + runs-on: ubuntu-20.04 + outputs: + run: ${{ steps.check.outputs.run }} + steps: + - name: check + id: check + uses: actions/github-script@v6 + with: + script: | + let run = false; - const isManualTrigger = context.payload.inputs && context.payload.inputs.release_branch && context.payload.inputs.release_branch != null; - - const isMergedMilestonedIssue = context.payload.issue && context.payload.issue.pull_request != null && context.payload.issue.pull_request.merged_at != null && context.payload.issue.milestone != null; - - const isMergedMilestonedPR = context.payload.pull_request && context.payload.pull_request != null && context.payload.pull_request.merged == true && context.payload.pull_request.milestone != null; + const isManualTrigger = context.payload.inputs && context.payload.inputs.release_branch && context.payload.inputs.release_branch != null; - const isBot = context.payload.pull_request && ( context.payload.pull_request.user.login == 'github-actions[bot]' || context.payload.pull_request.user.type == 'Bot' ); + const isMergedMilestonedIssue = context.payload.issue && context.payload.issue.pull_request != null && context.payload.issue.pull_request.merged_at != null && context.payload.issue.milestone != null; - if ( !isBot && ( isManualTrigger || isMergedMilestonedIssue || isMergedMilestonedPR ) ) { - console.log( "::set-output name=run::true" ); - } else { - console.log( "::set-output name=run::false" ); - } - prep: - name: Prep inputs - runs-on: ubuntu-20.04 - needs: verify - if: needs.verify.outputs.run == 'true' - outputs: - release: ${{ steps.prep-inputs.outputs.release }} - pr: ${{ steps.prep-inputs.outputs.pr }} - version: ${{ steps.prep-inputs.outputs.version }} - steps: - - name: Prep inputs - id: prep-inputs - uses: actions/github-script@v6 - with: - script: | - const event = ${{ toJSON( github.event ) }} + const isMergedMilestonedPR = context.payload.pull_request && context.payload.pull_request != null && context.payload.pull_request.merged == true && context.payload.pull_request.milestone != null; - // Means this workflow was triggered manually. - if ( event.inputs && event.inputs.release_branch ) { - const releaseBranch = '${{ inputs.release_branch }}' - const version = releaseBranch.replace( 'release/', '' ); + const isBot = context.payload.pull_request && ( context.payload.pull_request.user.login == 'github-actions[bot]' || context.payload.pull_request.user.type == 'Bot' ); - console.log( "::set-output name=version::" + version ) - console.log( "::set-output name=release::${{ inputs.release_branch }}" ) - } else if ( event.action === 'milestoned' ) { - const version = '${{ github.event.issue.milestone.title }}' - const release = version.substring( 0, 3 ) + if ( !isBot && ( isManualTrigger || isMergedMilestonedIssue || isMergedMilestonedPR ) ) { + core.setOutput( 'run', 'true' ); + } else { + core.setOutput( 'run', 'false' ); + } + prep: + name: Prep inputs + runs-on: ubuntu-20.04 + needs: verify + if: needs.verify.outputs.run == 'true' + outputs: + release: ${{ steps.prep-inputs.outputs.release }} + pr: ${{ steps.prep-inputs.outputs.pr }} + version: ${{ steps.prep-inputs.outputs.version }} + steps: + - name: Prep inputs + id: prep-inputs + uses: actions/github-script@v6 + with: + script: | + const event = ${{ toJSON( github.event ) }} - console.log( "::set-output name=version::" + version ) - console.log( "::set-output name=release::release/" + release ) - } else { - const version = '${{ github.event.pull_request.milestone.title }}' - const release = version.substring( 0, 3 ) + // Means this workflow was triggered manually. + if ( event.inputs && event.inputs.release_branch ) { + const releaseBranch = '${{ inputs.release_branch }}' + const version = releaseBranch.replace( 'release/', '' ) - console.log( "::set-output name=version::" + version ) - console.log( "::set-output name=release::release/" + release ) - } + core.setOutput( 'version', version ) + core.setOutput( 'release', releaseBranch ) + } else if ( event.action === 'milestoned' ) { + const version = '${{ github.event.issue.milestone.title }}' + const release = version.substring( 0, 3 ) - // Means this workflow was triggered manually. - if ( event.inputs && event.inputs.pull_requests ) { - console.log( "::set-output name=pr::${{ inputs.pull_requests }}" ) - } else if ( event.action === 'milestoned' ) { - console.log( "::set-output name=pr::${{ github.event.issue.number }}" ) - } else { - console.log( "::set-output name=pr::${{ github.event.pull_request.number }}" ) - } - check-release-branch-exists: - name: Check for existence of release branch - runs-on: ubuntu-20.04 - needs: prep - steps: - - name: Check for release branch - id: release-breanch-check - uses: actions/github-script@v6 - with: - script: | - // This will throw an error for non-200 responses, which prevents subsequent jobs from completing, as desired. - await github.request( 'GET /repos/{owner}/{repo}/branches/{branch}', { - owner: context.repo.owner, - repo: context.repo.repo, - branch: '${{ needs.prep.outputs.release }}', - } ); - cherry-pick-run: - name: Run cherry pick tool - runs-on: ubuntu-20.04 - needs: [ prep, check-release-branch-exists ] - if: success() - steps: - - name: Checkout release branch - uses: actions/checkout@v3 - with: - fetch-depth: 0 + core.setOutput( 'version', version ) + core.setOutput( 'release', `release/${release}` ) + } else { + const version = '${{ github.event.pull_request.milestone.title }}' + const release = version.substring( 0, 3 ) - - name: Git fetch the release branch - run: git fetch origin ${{ needs.prep.outputs.release }} + core.setOutput( 'version', version ) + core.setOutput( 'release', `release/${release}` ) + } - - name: Checkout release branch - run: git checkout ${{ needs.prep.outputs.release }} + // Means this workflow was triggered manually. + if ( event.inputs && event.inputs.pull_requests ) { + core.setOutput( 'pr', '${{ inputs.pull_requests }}' ) + } else if ( event.action === 'milestoned' ) { + core.setOutput( 'pr', '${{ github.event.issue.number }}' ) + } else { + core.setOutput( 'pr', '${{ github.event.pull_request.number }}' ) + } + check-release-branch-exists: + name: Check for existence of release branch + runs-on: ubuntu-20.04 + needs: prep + steps: + - name: Check for release branch + id: release-breanch-check + uses: actions/github-script@v6 + with: + script: | + // This will throw an error for non-200 responses, which prevents subsequent jobs from completing, as desired. + await github.request( 'GET /repos/{owner}/{repo}/branches/{branch}', { + owner: context.repo.owner, + repo: context.repo.repo, + branch: '${{ needs.prep.outputs.release }}', + } ); + cherry-pick-run: + name: Run cherry pick tool + runs-on: ubuntu-20.04 + permissions: + actions: write + contents: write + pull-requests: write + needs: [prep, check-release-branch-exists] + if: success() + steps: + - name: Checkout release branch + uses: actions/checkout@v3 + with: + fetch-depth: 0 - - name: Create a cherry pick branch based on release branch - run: git checkout -b cherry-pick-${{ needs.prep.outputs.version }}/${{ needs.prep.outputs.pr }} + - name: Git fetch the release branch + run: git fetch origin ${{ needs.prep.outputs.release }} - - name: Get commit sha from PR - id: commit-sha - uses: actions/github-script@v6 - with: - script: | - const pr = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: '${{ needs.prep.outputs.pr }}' - }) + - name: Checkout release branch + run: git checkout ${{ needs.prep.outputs.release }} - console.log( `::set-output name=sha::${ pr.data.merge_commit_sha }` ) + - name: Create a cherry pick branch based on release branch + run: git checkout -b cherry-pick-${{ needs.prep.outputs.version }}/${{ needs.prep.outputs.pr }} - - name: Cherry pick - run: | - git cherry-pick ${{ steps.commit-sha.outputs.sha }} + - name: Get commit sha from PR + id: commit-sha + uses: actions/github-script@v6 + with: + script: | + const pr = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: '${{ needs.prep.outputs.pr }}' + }) - - name: Generate changelog - id: changelog - uses: actions/github-script@v6 - with: - script: | - const fs = require( 'node:fs' ); + core.setOutput( 'sha', pr.data.merge_commit_sha ) - const changelogsToBeDeleted = [] - let changelogTxt = ''; + - name: Cherry pick + run: | + git cherry-pick ${{ steps.commit-sha.outputs.sha }} -m1 - const commit = await github.rest.repos.getCommit({ - owner: context.repo.owner, - repo: context.repo.repo, - ref: '${{ steps.commit-sha.outputs.sha }}' - }) + - name: Generate changelog + id: changelog + uses: actions/github-script@v6 + with: + script: | + const fs = require( 'node:fs' ); - for ( const file of commit.data.files ) { - if ( file.filename.match( 'plugins/woocommerce/changelog/' ) ) { - if ( changelogsToBeDeleted.indexOf( file.filename ) === -1 ) { - changelogsToBeDeleted.push( file.filename ); - } + const changelogsToBeDeleted = [] + let changelogTxt = ''; - let changelogEntry = ''; - let changelogEntryType = ''; + const commit = await github.rest.repos.getCommit({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: '${{ steps.commit-sha.outputs.sha }}' + }) - fs.readFile( './' + file.filename, 'utf-8', function( err, data ) { - if ( err ) { - console.error( err ); - } + for ( const file of commit.data.files ) { + if ( file.filename.match( 'plugins/woocommerce/changelog/' ) ) { + if ( changelogsToBeDeleted.indexOf( file.filename ) === -1 ) { + changelogsToBeDeleted.push( file.filename ); + } - const changelogEntryArr = data.split( "\n" ); - changelogEntryType = data.match( /Type: (.+)/i ); - changelogEntryType = changelogEntryType[ 1 ].charAt( 0 ).toUpperCase() + changelogEntryType[ 1 ].slice( 1 ); + let changelogEntry = ''; + let changelogEntryType = ''; - changelogEntry = changelogEntryArr.filter( el => { - return el !== null && typeof el !== 'undefined' && el !== ''; - } ); - changelogEntry = changelogEntry[ changelogEntry.length - 1 ]; + fs.readFile( './' + file.filename, 'utf-8', function( err, data ) { + if ( err ) { + console.error( err ); + } - // Check if changelogEntry is what we want. - if ( changelogEntry.length < 1 ) { - changelogEntry = false; - } + const changelogEntryArr = data.split( "\n" ); + changelogEntryType = data.match( /Type: (.+)/i ); + changelogEntryType = changelogEntryType[ 1 ].charAt( 0 ).toUpperCase() + changelogEntryType[ 1 ].slice( 1 ); - if ( changelogEntry.match( /significance:/i ) ) { - changelogEntry = false; - } + changelogEntry = changelogEntryArr.filter( el => { + return el !== null && typeof el !== 'undefined' && el !== ''; + } ); + changelogEntry = changelogEntry[ changelogEntry.length - 1 ]; - if ( changelogEntry.match( /type:/i ) ) { - changelogEntry = false; - } + // Check if changelogEntry is what we want. + if ( changelogEntry.length < 1 ) { + changelogEntry = false; + } - if ( changelogEntry.match( /comment:/i ) ) { - changelogEntry = false; - } - } ); + if ( changelogEntry.match( /significance:/i ) ) { + changelogEntry = false; + } - if ( changelogEntry === false ) { - continue; - } + if ( changelogEntry.match( /type:/i ) ) { + changelogEntry = false; + } - fs.readFile( './plugins/woocommerce/readme.txt', 'utf-8', function( err, data ) { - if ( err ) { - console.error( err ); - } + if ( changelogEntry.match( /comment:/i ) ) { + changelogEntry = false; + } - changelogTxt = data.split( "\n" ); - let isInRange = false; - let newChangelogTxt = []; + if ( ! changelogEntry ) { + return; + } - for ( const line of changelogTxt ) { - if ( isInRange === false && line === '== Changelog ==' ) { - isInRange = true; - } + fs.readFile( './plugins/woocommerce/readme.txt', 'utf-8', function( err, data ) { + if ( err ) { + console.error( err ); + } - if ( isInRange === true && line.match( /\*\*WooCommerce Blocks/ ) ) { - isInRange = false; - } + changelogTxt = data.split( "\n" ); + let isInRange = false; + let newChangelogTxt = []; - // Find the first match of the entry "Type". - if ( isInRange && line.match( `\\* ${changelogEntryType} -` ) ) { - newChangelogTxt.push( '* ' + changelogEntryType + ' - ' + changelogEntry + ` [#${{ needs.prep.outputs.pr }}](https://github.com/woocommerce/woocommerce/pull/${{ needs.prep.outputs.pr }})` ); - newChangelogTxt.push( line ); - isInRange = false; - continue; - } + for ( const line of changelogTxt ) { + if ( isInRange === false && line === '== Changelog ==' ) { + isInRange = true; + } - newChangelogTxt.push( line ); - } + if ( isInRange === true && line.match( /\*\*WooCommerce Blocks/ ) ) { + isInRange = false; + } - fs.writeFile( './plugins/woocommerce/readme.txt', newChangelogTxt.join( "\n" ), err => { - if ( err ) { - console.error( `Unable to generate the changelog entry for PR ${{ needs.prep.outputs.pr }}` ); - } - } ); - } ); - } - } + // Find the first match of the entry "Type". + if ( isInRange && line.match( `\\* ${changelogEntryType} -` ) ) { + newChangelogTxt.push( '* ' + changelogEntryType + ' - ' + changelogEntry + ` [#${{ needs.prep.outputs.pr }}](https://github.com/woocommerce/woocommerce/pull/${{ needs.prep.outputs.pr }})` ); + newChangelogTxt.push( line ); + isInRange = false; + continue; + } - console.log( `::set-output name=changelogsToBeDeleted::${ changelogsToBeDeleted }` ) + newChangelogTxt.push( line ); + } - - name: Delete changelog files from cherry pick branch - if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null - run: git rm ${{ steps.changelog.outputs.changelogsToBeDeleted }} + fs.writeFile( './plugins/woocommerce/readme.txt', newChangelogTxt.join( "\n" ), err => { + if ( err ) { + console.error( `Unable to generate the changelog entry for PR ${{ needs.prep.outputs.pr }}` ); + } + } ); + } ); + } ); + } + } - - name: Commit changes for cherry pick - if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null - run: git commit --no-verify -am "Prep for cherry pick ${{ needs.prep.outputs.pr }}" + core.setOutput( 'changelogsToBeDeleted', changelogsToBeDeleted.join( ' ' ) ) - - name: Push cherry pick branch up - run: git push origin cherry-pick-${{ needs.prep.outputs.version }}/${{ needs.prep.outputs.pr }} + - name: Delete changelog files from cherry pick branch + if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null + run: git rm ${{ steps.changelog.outputs.changelogsToBeDeleted }} - - name: Create the PR for cherry pick branch - id: cherry-pick-pr - uses: actions/github-script@v6 - with: - script: | - let cherryPickPRBody = "This PR cherry-picks the following PRs into the release branch:\n"; + - name: Commit changes for cherry pick + if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null + run: git commit --no-verify -am "Prep for cherry pick ${{ needs.prep.outputs.pr }}" - cherryPickPRBody = `${cherryPickPRBody}` + `* #${{ needs.prep.outputs.pr }}` + "\n"; + - name: Push cherry pick branch up + run: git push origin cherry-pick-${{ needs.prep.outputs.version }}/${{ needs.prep.outputs.pr }} - const pr = await github.rest.pulls.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: "Cherry pick ${{ needs.prep.outputs.pr }} into ${{ needs.prep.outputs.release }}", - head: "cherry-pick-${{ needs.prep.outputs.version }}/${{ needs.prep.outputs.pr }}", - base: "${{ needs.prep.outputs.release }}", - body: cherryPickPRBody - }) + - name: Create the PR for cherry pick branch + id: cherry-pick-pr + uses: actions/github-script@v6 + with: + script: | + let cherryPickPRBody = "This PR cherry-picks the following PRs into the release branch:\n"; - console.log( `::set-output name=cherry-pick-pr::${ pr.data.html_url }` ) - - name: Checkout trunk branch - if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null - run: git checkout trunk + cherryPickPRBody = `${cherryPickPRBody}` + `* #${{ needs.prep.outputs.pr }}` + "\n"; - - name: Create a branch based on trunk branch - if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null - run: git checkout -b delete-changelogs/${{ needs.prep.outputs.pr }} + const pr = await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: "Cherry pick ${{ needs.prep.outputs.pr }} into ${{ needs.prep.outputs.release }}", + head: "cherry-pick-${{ needs.prep.outputs.version }}/${{ needs.prep.outputs.pr }}", + base: "${{ needs.prep.outputs.release }}", + body: cherryPickPRBody + }) - - name: Delete changelogs from trunk - if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null - run: git rm ${{ steps.changelog.outputs.changelogsToBeDeleted }} + core.setOutput( 'cherry-pick-pr', pr.data.html_url ) - - name: Commit changes for deletion - if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null - run: git commit --no-verify -am "Delete changelog files for ${{ needs.prep.outputs.pr }}" + - name: Checkout trunk branch + if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null + run: git checkout trunk - - name: Push deletion branch up - if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null - run: git push origin delete-changelogs/${{ needs.prep.outputs.pr }} + - name: Create a branch based on trunk branch + if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null + run: git checkout -b delete-changelogs/${{ needs.prep.outputs.pr }} - - name: Create the PR for deletion branch - id: deletion-pr - if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null - uses: actions/github-script@v6 - with: - script: | - const pr = await github.rest.pulls.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: "Delete changelog files based on PR ${{ needs.prep.outputs.pr }}", - head: "delete-changelogs/${{ needs.prep.outputs.pr }}", - base: "trunk", - body: "Delete changelog files based on PR #${{ needs.prep.outputs.pr }}" - }) + - name: Delete changelogs from trunk + if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null + run: git rm ${{ steps.changelog.outputs.changelogsToBeDeleted }} - console.log( `::set-output name=deletion-pr::${ pr.data.html_url }` ) + - name: Commit changes for deletion + if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null + run: git commit --no-verify -am "Delete changelog files for ${{ needs.prep.outputs.pr }}" - - name: Notify Slack on failure - if: ${{ failure() && inputs.skipSlackPing != true }} - uses: archive/github-actions-slack@v2.0.0 - with: - slack-bot-user-oauth-access-token: ${{ secrets.CODE_FREEZE_BOT_TOKEN }} - slack-channel: ${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }} - slack-text: | - :warning-8c: Code freeze violation. PR(s) created that breaks the Code Freeze for '${{ needs.prep.outputs.release }}' :ice_cube: + - name: Push deletion branch up + if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null + run: git push origin delete-changelogs/${{ needs.prep.outputs.pr }} - An attempt to cherry pick PR(s) into outgoing release '${{ needs.prep.outputs.release }}' has failed. This could be due to a merge conflict or something else that requires manual attention. Please check: https://github.com/woocommerce/woocommerce/pull/${{ needs.prep.outputs.pr }} + - name: Create the PR for deletion branch + id: deletion-pr + if: steps.changelog.outputs.changelogsToBeDeleted != '' && steps.changelog.outputs.changelogsToBeDeleted != null + uses: actions/github-script@v6 + with: + script: | + const pr = await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: "Delete changelog files based on PR ${{ needs.prep.outputs.pr }}", + head: "delete-changelogs/${{ needs.prep.outputs.pr }}", + base: "trunk", + body: "Delete changelog files based on PR #${{ needs.prep.outputs.pr }}" + }) - - name: Notify Slack on success - if: ${{ success() && inputs.skipSlackPing != true }} - uses: archive/github-actions-slack@v2.0.0 - with: - slack-bot-user-oauth-access-token: ${{ secrets.CODE_FREEZE_BOT_TOKEN }} - slack-channel: ${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }} - slack-text: | - :warning-8c: Code freeze violation. PR(s) created that breaks the Code Freeze for '${{ needs.prep.outputs.release }}' :ice_cube: - - Release lead please review: + core.setOutput( 'deletion-pr', pr.data.html_url ) - ${{ steps.cherry-pick-pr.outputs.cherry-pick-pr }} - ${{ steps.deletion-pr.outputs.deletion-pr }} + - name: Notify Slack on failure + if: ${{ failure() && inputs.skipSlackPing != true }} + uses: archive/github-actions-slack@v2.0.0 + with: + slack-bot-user-oauth-access-token: ${{ secrets.CODE_FREEZE_BOT_TOKEN }} + slack-channel: ${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }} + slack-text: | + :warning-8c: Code freeze violation. PR(s) created that breaks the Code Freeze for '${{ needs.prep.outputs.release }}' :ice_cube: + + An attempt to cherry pick PR(s) into outgoing release '${{ needs.prep.outputs.release }}' has failed. This could be due to a merge conflict or something else that requires manual attention. Please check: https://github.com/woocommerce/woocommerce/pull/${{ needs.prep.outputs.pr }} + + - name: Notify Slack on success + if: ${{ success() && inputs.skipSlackPing != true }} + uses: archive/github-actions-slack@v2.0.0 + with: + slack-bot-user-oauth-access-token: ${{ secrets.CODE_FREEZE_BOT_TOKEN }} + slack-channel: ${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }} + slack-text: | + :warning-8c: Code freeze violation. PR(s) created that breaks the Code Freeze for '${{ needs.prep.outputs.release }}' :ice_cube: + + Release lead please review: + + ${{ steps.cherry-pick-pr.outputs.cherry-pick-pr }} + ${{ steps.deletion-pr.outputs.deletion-pr }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 512e609e1e0..7e58e5508c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,77 +1,80 @@ name: Run CI -on: - push: - branches: - - trunk - - 'release/**' - workflow_dispatch: +on: + push: + branches: + - trunk + - 'release/**' + workflow_dispatch: defaults: - run: - shell: bash -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + run: + shell: bash +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + jobs: - test: - name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} - timeout-minutes: 30 - runs-on: ubuntu-20.04 - continue-on-error: ${{ matrix.wp == 'nightly' }} - strategy: - fail-fast: false - matrix: - php: [ '7.4', '8.0' ] - wp: [ 'latest' ] - include: - - wp: nightly - php: '7.4' - - wp: '5.9' - php: 7.4 - - wp: '5.8' - php: 7.4 - services: - database: - image: mysql:5.6 - env: - MYSQL_ROOT_PASSWORD: root - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 - steps: - - uses: actions/checkout@v3 + test: + name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} + timeout-minutes: 30 + runs-on: ubuntu-20.04 + permissions: + contents: read + continue-on-error: ${{ matrix.wp == 'nightly' }} + strategy: + fail-fast: false + matrix: + php: ['7.4', '8.0'] + wp: ['latest'] + include: + - wp: nightly + php: '7.4' + - wp: '6.1' + php: 7.4 + services: + database: + image: mysql:5.6 + env: + MYSQL_ROOT_PASSWORD: root + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 + steps: + - uses: actions/checkout@v3 - - name: Setup WooCommerce Monorepo - uses: ./.github/actions/setup-woocommerce-monorepo - with: - build-filters: woocommerce + - name: Setup WooCommerce Monorepo + uses: ./.github/actions/setup-woocommerce-monorepo + with: + build-filters: woocommerce - - name: Tool versions - run: | - php --version - composer --version + - name: Tool versions + run: | + php --version + composer --version - - name: Build Admin feature config - working-directory: plugins/woocommerce - run: pnpm run build:feature-config + - name: Build Admin feature config + working-directory: plugins/woocommerce + run: pnpm run build:feature-config - - name: Add PHP8 Compatibility. - run: | - if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then - cd plugins/woocommerce - curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip - unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip - composer bin phpunit config --unset platform - composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}' - composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs - rm -rf ./vendor/phpunit/ - composer dump-autoload - fi + - name: Add PHP8 Compatibility. + run: | + if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then + cd plugins/woocommerce + curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip + unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip + composer bin phpunit config --unset platform + composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}' + composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs + rm -rf ./vendor/phpunit/ + composer dump-autoload + fi - - name: Init DB and WP - working-directory: plugins/woocommerce - run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }} + - name: Init DB and WP + working-directory: plugins/woocommerce + run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }} - - name: Run tests - working-directory: plugins/woocommerce - run: pnpm run test --color + - name: Run tests + working-directory: plugins/woocommerce + run: pnpm run test --color diff --git a/.github/workflows/community-label.yml b/.github/workflows/community-label.yml index 221753b62e3..3361dfa033b 100644 --- a/.github/workflows/community-label.yml +++ b/.github/workflows/community-label.yml @@ -1,38 +1,46 @@ name: Add Community Label on: - pull_request_target: - types: [opened] - issues: - types: [opened] - + pull_request_target: + types: [opened] + issues: + types: [opened] -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} jobs: - verify: - name: Verify - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 + verify: + name: Verify + runs-on: ubuntu-20.04 + permissions: + contents: read + pull-requests: write + issues: write + steps: + - uses: actions/checkout@v3 - - name: Setup Node.js - uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 + - name: Setup Node.js + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c - - name: Install Octokit - run: npm --prefix .github/workflows/scripts install @octokit/action + - name: Install Octokit + run: npm --prefix .github/workflows/scripts install @octokit/action - - name: Check if user is a community contributor - id: check - run: node .github/workflows/scripts/is-community-contributor.js - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: "If community PR, assign a reviewer" - if: github.event.pull_request && steps.check.outputs.is-community == 'yes' - uses: shufo/auto-assign-reviewer-by-files@f5f3db9ef06bd72ab6978996988c6462cbdaabf6 - with: - config: ".github/project-community-pr-assigner.yml" - token: ${{ secrets.PR_ASSIGN_TOKEN }} + - name: Install Actions Core + run: npm --prefix .github/workflows/scripts install @actions/core + + - name: Check if user is a community contributor + id: check + run: node .github/workflows/scripts/is-community-contributor.js + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: 'If community PR, assign a reviewer' + if: github.event.pull_request && steps.check.outputs.is-community == 'yes' + uses: shufo/auto-assign-reviewer-by-files@f5f3db9ef06bd72ab6978996988c6462cbdaabf6 + with: + config: '.github/project-community-pr-assigner.yml' + token: ${{ secrets.PR_ASSIGN_TOKEN }} diff --git a/.github/workflows/cot-build-and-e2e-tests-daily.yml b/.github/workflows/cot-build-and-e2e-tests-daily.yml index 51cd250ce12..7ef685e22fe 100644 --- a/.github/workflows/cot-build-and-e2e-tests-daily.yml +++ b/.github/workflows/cot-build-and-e2e-tests-daily.yml @@ -8,10 +8,14 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: cot-e2e-tests-run: name: Runs E2E tests with COT enabled. runs-on: ubuntu-20.04 + permissions: + contents: read 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 @@ -66,6 +70,8 @@ jobs: cot-api-tests-run: name: Runs API tests with COT enabled. runs-on: ubuntu-20.04 + permissions: + contents: read env: ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/api-test-report/allure-results ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/api-test-report/allure-report @@ -124,6 +130,8 @@ jobs: contains( needs.*.result, 'failure' ) ) runs-on: ubuntu-20.04 + permissions: + contents: read needs: [cot-api-tests-run, cot-e2e-tests-run] steps: - name: Create dirs @@ -165,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 }} @@ -173,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 }} diff --git a/.github/workflows/cot-pr-build-and-e2e-tests.yml b/.github/workflows/cot-pr-build-and-e2e-tests.yml index e5e6e4bd4cc..02cfb498434 100644 --- a/.github/workflows/cot-pr-build-and-e2e-tests.yml +++ b/.github/workflows/cot-pr-build-and-e2e-tests.yml @@ -1,18 +1,20 @@ name: Run tests against PR in an environment with COT enabled on: pull_request: - types: [labeled] workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: cot-e2e-tests-run: name: Runs E2E tests with COT enabled. - if: "${{ github.event_name == 'workflow_dispatch' || github.event.label.name == 'focus: custom order tables' }}" runs-on: ubuntu-20.04 + permissions: + contents: read 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 @@ -66,8 +68,9 @@ jobs: cot-api-tests-run: name: Runs API tests with COT enabled. - if: "${{ github.event_name == 'workflow_dispatch' || github.event.label.name == 'focus: custom order tables' }}" runs-on: ubuntu-20.04 + permissions: + contents: read env: ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/api-test-report/allure-results ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/api-test-report/allure-report @@ -115,100 +118,3 @@ jobs: ${{ env.ALLURE_REPORT_DIR }} if-no-files-found: ignore retention-days: 5 - - test-summary: - name: Post test results - if: | - always() && - ! github.event.pull_request.head.repo.fork && - ( - contains( needs.*.result, 'success' ) || - contains( needs.*.result, 'failure' ) - ) - runs-on: ubuntu-20.04 - needs: [cot-api-tests-run, cot-e2e-tests-run] - steps: - - name: Create dirs - run: | - mkdir -p repo - mkdir -p artifacts/api - mkdir -p artifacts/e2e - mkdir -p output - - - name: Checkout code - uses: actions/checkout@v3 - with: - path: repo - - - name: Download API test report artifact - uses: actions/download-artifact@v3 - with: - name: api-test-report---pr-${{ github.event.number }} - path: artifacts/api - - - name: Download Playwright E2E test report artifact - uses: actions/download-artifact@v3 - with: - name: e2e-test-report---pr-${{ github.event.number }} - path: artifacts/e2e - - - name: Prepare test summary - id: prepare-test-summary - uses: actions/github-script@v6 - env: - API_SUMMARY_PATH: ${{ github.workspace }}/artifacts/api/allure-report/widgets/summary.json - E2E_PW_SUMMARY_PATH: ${{ github.workspace }}/artifacts/e2e/allure-report/widgets/summary.json - PR_NUMBER: ${{ github.event.number }} - SHA: ${{ github.event.pull_request.head.sha }} - with: - result-encoding: string - script: | - const script = require( './repo/.github/workflows/scripts/prepare-test-summary.js' ) - return await script( { core } ) - - - name: Find PR comment by github-actions[bot] - uses: peter-evans/find-comment@v2 - id: find-comment - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: Test Results Summary - - - name: Create or update PR comment - uses: peter-evans/create-or-update-comment@v2 - with: - comment-id: ${{ steps.find-comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - body: ${{ steps.prepare-test-summary.outputs.result }} - edit-mode: replace - - publish-test-reports: - name: Publish test reports - if: | - always() && - ! github.event.pull_request.head.repo.fork && - ( - contains( needs.*.result, 'success' ) || - contains( needs.*.result, 'failure' ) - ) - runs-on: ubuntu-20.04 - needs: [cot-api-tests-run, cot-e2e-tests-run] - env: - GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }} - PR_NUMBER: ${{ github.event.number }} - RUN_ID: ${{ github.run_id }} - COMMIT_SHA: ${{ github.event.pull_request.head.sha }} - steps: - - name: Publish test reports - env: - API_ARTIFACT: api-test-report---pr-${{ github.event.number }} - E2E_ARTIFACT: e2e-test-report---pr-${{ github.event.number }} - run: | - gh workflow run publish-test-reports-pr.yml \ - -f run_id=$RUN_ID \ - -f api_artifact=$API_ARTIFACT \ - -f e2e_artifact=$E2E_ARTIFACT \ - -f pr_number=$PR_NUMBER \ - -f commit_sha=$COMMIT_SHA \ - -f s3_root=public \ - --repo woocommerce/woocommerce-test-reports diff --git a/.github/workflows/mirrors.yml b/.github/workflows/mirrors.yml index b3cb91069ee..37ccec1fec0 100644 --- a/.github/workflows/mirrors.yml +++ b/.github/workflows/mirrors.yml @@ -4,11 +4,15 @@ on: branches: ["trunk", "release/**"] workflow_dispatch: +permissions: {} + jobs: build: if: github.repository == 'woocommerce/woocommerce' name: Build WooCommerce zip runs-on: ubuntu-20.04 + permissions: + contents: read steps: - uses: actions/checkout@v3 @@ -35,6 +39,8 @@ jobs: name: Push to Mirror needs: [build] runs-on: ubuntu-20.04 + permissions: + contents: read steps: - name: Create directories run: | diff --git a/.github/workflows/nightly-builds.yml b/.github/workflows/nightly-builds.yml index 06227ac6615..64903cb5075 100644 --- a/.github/workflows/nightly-builds.yml +++ b/.github/workflows/nightly-builds.yml @@ -3,6 +3,9 @@ on: schedule: - cron: '0 0 * * *' # Run at 12 AM UTC. workflow_dispatch: + +permissions: {} + jobs: build: if: github.repository_owner == 'woocommerce' @@ -12,6 +15,8 @@ jobs: matrix: build: [trunk] runs-on: ubuntu-20.04 + permissions: + contents: write steps: - uses: actions/checkout@v3 with: @@ -40,6 +45,8 @@ jobs: update: name: Update nightly tag commit ref runs-on: ubuntu-20.04 + permissions: + contents: write steps: - name: Update nightly tag uses: richardsimko/github-tag-action@v1.0.5 diff --git a/.github/workflows/package-release.yml b/.github/workflows/package-release.yml index 8f4a78d5424..1400a50dbe5 100644 --- a/.github/workflows/package-release.yml +++ b/.github/workflows/package-release.yml @@ -6,10 +6,15 @@ on: description: 'Enter a specific package to release, or releases separated by commas, ie @woocommerce/components,@woocommerce/number. Leaving this input blank will release all eligible packages.' required: false default: '-a' + +permissions: {} + jobs: release: name: Run packages release script runs-on: ubuntu-20.04 + permissions: + contents: read steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml index c9454db35ef..6f2b9bb284b 100644 --- a/.github/workflows/post-release.yml +++ b/.github/workflows/post-release.yml @@ -1,7 +1,7 @@ name: Run post release processes -on: - release: - types: [released] +on: + release: + types: [released] env: GIT_COMMITTER_NAME: 'WooCommerce Bot' @@ -9,120 +9,125 @@ env: GIT_AUTHOR_NAME: 'WooCommerce Bot' GIT_AUTHOR_EMAIL: 'no-reply@woocommerce.com' +permissions: {} + jobs: - changelog-version-update: - name: Update changelog and version - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 + changelog-version-update: + name: Update changelog and version + runs-on: ubuntu-20.04 + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v3 - - name: Git fetch trunk branch - run: git fetch origin trunk + - name: Git fetch trunk branch + run: git fetch origin trunk - - name: Copy readme.txt to vm root - run: cp ./plugins/woocommerce/readme.txt ../../readme.txt + - name: Copy readme.txt to vm root + run: cp ./plugins/woocommerce/readme.txt ../../readme.txt - - name: Switch to trunk branch - run: git checkout trunk + - name: Switch to trunk branch + run: git checkout trunk - - name: Create a new branch based on trunk - run: git checkout -b prep/post-release-tasks-${{ github.event.release.tag_name }} + - name: Create a new branch based on trunk + run: git checkout -b prep/post-release-tasks-${{ github.event.release.tag_name }} - - name: Check if we need to continue processing - uses: actions/github-script@v6 - id: check - with: - script: | - const fs = require( 'node:fs' ); - const version = ${{ toJSON( github.event.release.tag_name ) }} + - name: Check if we need to continue processing + uses: actions/github-script@v6 + id: check + with: + script: | + const fs = require( 'node:fs' ); + const version = ${{ toJSON( github.event.release.tag_name ) }} - fs.readFile( './plugins/woocommerce/readme.txt', 'utf-8', function( err, data ) { - if ( err ) { - console.error( err ); - } + fs.readFile( './plugins/woocommerce/readme.txt', 'utf-8', function( err, data ) { + if ( err ) { + console.error( err ); + } - const regex = /Stable\stag:\s(\d+\.\d+\.\d+)/; + const regex = /Stable\stag:\s(\d+\.\d+\.\d+)/; - const stableVersion = data.match( regex )[1]; + const stableVersion = data.match( regex )[1]; - // If the release version is less than stable version we can bail. - if ( version.localeCompare( stableVersion, undefined, { numeric: true, sensitivity: 'base' } ) == -1 ) { - console.log( 'Release version is less than stable version. No automated action taken. A manual process is required.' ); - console.log( `::set-output name=continue::false` ) - return; - } else { - console.log( `::set-output name=continue::true` ) - } - } ) + // If the release version is less than stable version we can bail. + if ( version.localeCompare( stableVersion, undefined, { numeric: true, sensitivity: 'base' } ) == -1 ) { + console.log( 'Release version is less than stable version. No automated action taken. A manual process is required.' ); + core.setOutput( 'continue', 'false' ) + return; + } else { + core.setOutput( 'continue', 'true' ) + } + } ) - - name: Update changelog.txt entries - uses: actions/github-script@v6 - id: update-entries - if: steps.check.outputs.continue == 'true' - with: - script: | - const fs = require( 'node:fs' ); - const version = ${{ toJSON( github.event.release.tag_name ) }} + - name: Update changelog.txt entries + uses: actions/github-script@v6 + id: update-entries + if: steps.check.outputs.continue == 'true' + with: + script: | + const fs = require( 'node:fs' ); + const version = ${{ toJSON( github.event.release.tag_name ) }} - // Read the saved readme.txt file from earlier. - fs.readFile( '../../readme.txt', 'utf-8', function( err, readme ) { - if ( err ) { - console.log( `::set-output name=continue::false` ) - console.error( err ); - } + // Read the saved readme.txt file from earlier. + fs.readFile( '../../readme.txt', 'utf-8', function( err, readme ) { + if ( err ) { + core.setOutput( 'continue', 'false' ); + console.error( err ); + } - const regex = /(== Changelog ==[\s\S]+)\s{2}\[See changelog for all versions\]\(https:\/\/raw\.githubusercontent\.com\/woocommerce\/woocommerce\/trunk\/changelog\.txt\)\./; + const regex = /(== Changelog ==[\s\S]+)\s{2}\[See changelog for all versions\]\(https:\/\/raw\.githubusercontent\.com\/woocommerce\/woocommerce\/trunk\/changelog\.txt\)\./; - const entries = readme.match( regex )[1]; + const entries = readme.match( regex )[1]; - fs.readFile( './changelog.txt', 'utf-8', function( err, changelog ) { - if ( err ) { - console.log( `::set-output name=continue::false` ) - console.error( err ); - } + fs.readFile( './changelog.txt', 'utf-8', function( err, changelog ) { + if ( err ) { + core.setOutput( 'continue', 'false' ); + console.error( err ); + } - const regex = /== Changelog ==/; + const regex = /== Changelog ==/; - const updatedChangelog = changelog.replace( regex, entries ); + const updatedChangelog = changelog.replace( regex, entries ); - fs.writeFile( './changelog.txt', updatedChangelog, err => { - if ( err ) { - console.log( `::set-output name=continue::false` ) - console.error( 'Unable to update changelog entries in changelog.txt' ); - } + fs.writeFile( './changelog.txt', updatedChangelog, err => { + if ( err ) { + core.setOutput( 'continue', 'false' ); + console.error( 'Unable to update changelog entries in changelog.txt' ); + } - console.log( `::set-output name=continue::true` ) - } ) - } ) - } ) + core.setOutput( 'continue', 'true' ); + } ) + } ) + } ) - - name: Commit changes - if: steps.update-entries.outputs.continue == 'true' - run: git commit -am "Prep trunk post release ${{ github.event.release.tag_name }}" + - name: Commit changes + if: steps.update-entries.outputs.continue == 'true' + run: git commit -am "Prep trunk post release ${{ github.event.release.tag_name }}" - - name: Push branch up - if: steps.update-entries.outputs.continue == 'true' - run: git push origin prep/post-release-tasks-${{ github.event.release.tag_name }} + - name: Push branch up + if: steps.update-entries.outputs.continue == 'true' + run: git push origin prep/post-release-tasks-${{ github.event.release.tag_name }} - - name: Create the PR - if: steps.update-entries.outputs.continue == 'true' - uses: actions/github-script@v6 - with: - script: | - const body = "This PR updates the changelog.txt entries based on the latest release: ${{ github.event.release.tag_name }}" + - name: Create the PR + if: steps.update-entries.outputs.continue == 'true' + uses: actions/github-script@v6 + with: + script: | + const body = "This PR updates the changelog.txt entries based on the latest release: ${{ github.event.release.tag_name }}" - const pr = await github.rest.pulls.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: "Update changelog.txt from release ${{ github.event.release.tag_name }}", - head: "prep/post-release-tasks-${{ github.event.release.tag_name }}", - base: "trunk", - body: body - }) + const pr = await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: "Update changelog.txt from release ${{ github.event.release.tag_name }}", + head: "prep/post-release-tasks-${{ github.event.release.tag_name }}", + base: "trunk", + body: body + }) - const prCreated = await github.rest.pulls.requestReviewers({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: pr.data.number, - reviewers: ["${{ github.event.release.author.login }}"] - }) + const prCreated = await github.rest.pulls.requestReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.data.number, + reviewers: ["${{ github.event.release.author.login }}"] + }) diff --git a/.github/workflows/pr-build-and-e2e-tests.yml b/.github/workflows/pr-build-and-e2e-tests.yml index a5f7a7afe31..0ddad0d4617 100644 --- a/.github/workflows/pr-build-and-e2e-tests.yml +++ b/.github/workflows/pr-build-and-e2e-tests.yml @@ -1,19 +1,26 @@ name: Run tests against PR on: - pull_request: workflow_dispatch: + pull_request: + paths-ignore: + - '**/changelog/**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: e2e-tests-run: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} name: Runs E2E tests. runs-on: ubuntu-20.04 + permissions: + contents: read 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 + 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 outputs: E2E_GRAND_TOTAL: ${{ steps.count_e2e_total.outputs.E2E_GRAND_TOTAL }} steps: @@ -25,8 +32,9 @@ jobs: - name: Load docker images and start containers. working-directory: plugins/woocommerce env: - ENABLE_HPOS: 0 - run: pnpm env:test --filter=woocommerce + ENABLE_HPOS: 0 + WP_ENV_PHP_VERSION: 7.4 + run: pnpm run env:test - name: Download and install Chromium browser. working-directory: plugins/woocommerce @@ -39,7 +47,7 @@ jobs: TOTAL_STR=$(pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js --list | grep "Total:") NO_PREFIX=${TOTAL_STR#*"Total: "} COUNT=${NO_PREFIX%" tests in"*} - echo "::set-output name=E2E_GRAND_TOTAL::$COUNT" + echo "E2E_GRAND_TOTAL=$COUNT" >> $GITHUB_OUTPUT - name: Run Playwright E2E tests. timeout-minutes: 60 @@ -77,10 +85,13 @@ jobs: api-tests-run: name: Runs API tests. + if: github.event.pull_request.user.login != 'github-actions[bot]' runs-on: ubuntu-20.04 + permissions: + contents: read env: - ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/api-test-report/allure-results - 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/test-results/allure-results + ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-report steps: - uses: actions/checkout@v3 @@ -90,7 +101,7 @@ jobs: - name: Load docker images and start containers. working-directory: plugins/woocommerce env: - ENABLE_HPOS: 0 + ENABLE_HPOS: 0 run: pnpm env:test --filter=woocommerce - name: Run Playwright API tests. @@ -127,7 +138,10 @@ jobs: k6-tests-run: name: Runs k6 Performance tests + if: github.event.pull_request.user.login != 'github-actions[bot]' runs-on: ubuntu-20.04 + permissions: + contents: read steps: - uses: actions/checkout@v3 @@ -137,7 +151,7 @@ jobs: - name: Load docker images and start containers. working-directory: plugins/woocommerce env: - ENABLE_HPOS: 0 + ENABLE_HPOS: 0 run: | pnpm env:dev --filter=woocommerce pnpm env:performance-init --filter=woocommerce @@ -155,12 +169,17 @@ jobs: if: | always() && ! github.event.pull_request.head.repo.fork && + github.event.pull_request.user.login != 'github-actions[bot]' && ( contains( needs.*.result, 'success' ) || contains( needs.*.result, 'failure' ) ) runs-on: ubuntu-20.04 needs: [api-tests-run, e2e-tests-run] + permissions: + contents: read + issues: write + pull-requests: write env: E2E_GRAND_TOTAL: ${{needs.e2e-tests-run.outputs.E2E_GRAND_TOTAL}} steps: @@ -203,7 +222,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 }} @@ -211,7 +230,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 }} @@ -223,6 +242,7 @@ jobs: if: | always() && ! github.event.pull_request.head.repo.fork && + github.event.pull_request.user.login != 'github-actions[bot]' && ( contains( needs.*.result, 'success' ) || contains( needs.*.result, 'failure' ) diff --git a/.github/workflows/pr-build-live-branch.yml b/.github/workflows/pr-build-live-branch.yml new file mode 100644 index 00000000000..220c7ce3b75 --- /dev/null +++ b/.github/workflows/pr-build-live-branch.yml @@ -0,0 +1,90 @@ +name: Build Live Branch +on: + pull_request: + paths-ignore: + - '**/changelog/**' + +concurrency: + # Cancel concurrent jobs on pull_request but not push, by including the run_id in the concurrency group for the latter. + group: build-${{ github.event_name == 'push' && github.run_id || 'pr' }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + build: + if: github.repository_owner == 'woocommerce' && github.event.pull_request.user.login != 'github-actions[bot]' + runs-on: ubuntu-20.04 + permissions: + contents: read + steps: + - uses: actions/checkout@v3 + + - name: Get current version + id: version + uses: actions/github-script@v6.3.3 + with: + script: + const { getVersion } = require( './.github/workflows/scripts/get-plugin-version' ); + const version = await getVersion( 'woocommerce' ); + core.setOutput( 'version', version ); + + - name: Setup WooCommerce Monorepo + uses: ./.github/actions/setup-woocommerce-monorepo + with: + build: false + + - name: Prepare plugin zips + id: prepare + env: + CURRENT_VERSION: ${{ steps.version.outputs.version }} + run: | + + # Current version must compare greather than any previously used current version for this PR. + # Assume GH run IDs are monotonic. + VERSUFFIX="${GITHUB_RUN_ID}-g$(git rev-parse --short HEAD)" + + CURRENT_VERSION="$CURRENT_VERSION-$VERSUFFIX" + + sed -i -e 's/Version: .*$/Version: '"$CURRENT_VERSION"'/' "$GITHUB_WORKSPACE/plugins/woocommerce/woocommerce.php" + echo "$CURRENT_VERSION" > "$GITHUB_WORKSPACE/plugins/woocommerce/version.txt" + + cd "$GITHUB_WORKSPACE/plugins/woocommerce" + bash bin/build-zip.sh + + mkdir "$GITHUB_WORKSPACE/zips" + cp "$GITHUB_WORKSPACE/plugins/woocommerce/woocommerce.zip" "$GITHUB_WORKSPACE/zips/woocommerce.zip" + cd "$GITHUB_WORKSPACE/zips" + unzip woocommerce.zip + rm woocommerce.zip + mv woocommerce woocommerce-dev + zip -q -r "woocommerce-dev.zip" "woocommerce-dev/" + rm -fR "$GITHUB_WORKSPACE/zips/woocommerce-dev" + + # Plugin data is passed as a JSON object. + PLUGIN_DATA="{}" + PLUGIN_DATA=$( jq -c --arg slug "woocommerce" --arg ver "$CURRENT_VERSION" '.[ $slug ] = { version: $ver }' <<<"$PLUGIN_DATA" ) + echo "plugin-data=$PLUGIN_DATA" >> $GITHUB_OUTPUT + + - name: Create plugins artifact + uses: actions/upload-artifact@v3 + if: steps.prepare.outputs.plugin-data != '{}' + with: + name: plugins + path: zips + # Only need to retain for a day since the beta builder slurps it up to distribute. + retention-days: 1 + + - name: Inform Beta Download webhook + if: steps.prepare.outputs.plugin-data != '{}' + env: + SECRET: ${{ secrets.WOOBETA_SECRET }} + PLUGIN_DATA: ${{ steps.prepare.outputs.plugin-data }} + PR: ${{ github.event.number }} + run: | + curl -v --fail -L \ + --url "https://betadownload.jetpack.me/gh-action.php?run_id=$GITHUB_RUN_ID&pr=$PR&commit=$GITHUB_SHA" \ + --form-string "repo=$GITHUB_REPOSITORY" \ + --form-string "branch=${GITHUB_REF#refs/heads/}" \ + --form-string "plugins=$PLUGIN_DATA" \ + --form-string "secret=$SECRET" diff --git a/.github/workflows/pr-code-coverage.yml b/.github/workflows/pr-code-coverage.yml index 4f8e67820c1..3df9ef1d464 100644 --- a/.github/workflows/pr-code-coverage.yml +++ b/.github/workflows/pr-code-coverage.yml @@ -1,54 +1,61 @@ name: Run code coverage on PR on: - pull_request: - workflow_dispatch: + pull_request: + paths-ignore: + - '**/changelog/**' + workflow_dispatch: defaults: - run: - shell: bash -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + run: + shell: bash +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + jobs: - test: - name: Code coverage (PHP 7.4, WP Latest) - timeout-minutes: 30 - runs-on: ubuntu-20.04 - services: - database: - image: mysql:5.6 - env: - MYSQL_ROOT_PASSWORD: root - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 100 + test: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} + name: Code coverage (PHP 7.4, WP Latest) + timeout-minutes: 30 + runs-on: ubuntu-20.04 + permissions: + contents: read + services: + database: + image: mysql:5.6 + env: + MYSQL_ROOT_PASSWORD: root + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 100 - - name: Setup WooCommerce Monorepo - uses: ./.github/actions/setup-woocommerce-monorepo + - name: Setup WooCommerce Monorepo + uses: ./.github/actions/setup-woocommerce-monorepo - - name: Tool versions - run: | - php --version - composer --version + - name: Tool versions + run: | + php --version + composer --version - - name: Build Admin feature config - working-directory: plugins/woocommerce - run: - pnpm run build:feature-config + - name: Build Admin feature config + working-directory: plugins/woocommerce + run: pnpm run build:feature-config - - name: Init DB and WP - working-directory: plugins/woocommerce - run: bash tests/bin/install.sh woo_test root root 127.0.0.1 latest + - name: Init DB and WP + working-directory: plugins/woocommerce + run: bash tests/bin/install.sh woo_test root root 127.0.0.1 latest - - name: Run unit tests with code coverage. Allow to fail. - working-directory: plugins/woocommerce - run: | - RUN_CODE_COVERAGE=1 bash tests/bin/phpunit.sh - exit 0 - - - name: Send code coverage to Codecov. - run: | - bash <(curl -s https://codecov.io/bash) + - name: Run unit tests with code coverage. Allow to fail. + working-directory: plugins/woocommerce + run: | + RUN_CODE_COVERAGE=1 bash tests/bin/phpunit.sh + exit 0 + + - name: Send code coverage to Codecov. + run: | + bash <(curl -s https://codecov.io/bash) diff --git a/.github/workflows/pr-code-sniff.yml b/.github/workflows/pr-code-sniff.yml index 7a8254850d8..6026b2cd18e 100644 --- a/.github/workflows/pr-code-sniff.yml +++ b/.github/workflows/pr-code-sniff.yml @@ -1,41 +1,55 @@ name: Run code sniff on PR on: - pull_request + pull_request: + paths-ignore: + - '**/changelog/**' defaults: - run: - shell: bash -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + run: + shell: bash +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +env: + PHPCS: ./plugins/woocommerce/vendor/bin/phpcs # Run WooCommerce phpcs setup in phpcs-changed instead of default + +permissions: {} + jobs: - test: - name: Code sniff (PHP 7.4, WP Latest) - timeout-minutes: 15 - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Get Changed Files - id: changed-files - uses: tj-actions/changed-files@v32 - with: - files: | - **/*.php + test: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} + name: Code sniff (PHP 7.4, WP Latest) + timeout-minutes: 15 + runs-on: ubuntu-20.04 + permissions: + contents: read + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 - - name: Setup WooCommerce Monorepo - if: steps.changed-files.outputs.any_changed == 'true' - uses: ./.github/actions/setup-woocommerce-monorepo - with: - build: false + - name: Get Changed Files + id: changed-files + uses: tj-actions/changed-files@v32 + with: + files: | + **/*.php - - name: Tool versions - if: steps.changed-files.outputs.any_changed == 'true' - run: | - php --version - composer --version + - name: Setup WooCommerce Monorepo + if: steps.changed-files.outputs.any_changed == 'true' + uses: ./.github/actions/setup-woocommerce-monorepo + with: + build: false - - name: Run PHPCS - if: steps.changed-files.outputs.any_changed == 'true' - run: ./plugins/woocommerce/vendor/bin/phpcs -n -q --report=checkstyle ${{ steps.changed-files.outputs.all_changed_files }} | cs2pr + - name: Tool versions + if: steps.changed-files.outputs.any_changed == 'true' + run: | + php --version + composer --version + phpcs-changed --version + + - name: Run PHPCS + if: steps.changed-files.outputs.any_changed == 'true' + run: | + HEAD_REF=$(git rev-parse HEAD) + git checkout $HEAD_REF + phpcs-changed -s --git --git-base ${{ github.base_ref }} ${{ steps.changed-files.outputs.all_changed_files }} diff --git a/.github/workflows/pr-highlight-changes.yml b/.github/workflows/pr-highlight-changes.yml index c6a0186279e..a43759ca526 100644 --- a/.github/workflows/pr-highlight-changes.yml +++ b/.github/workflows/pr-highlight-changes.yml @@ -1,9 +1,18 @@ -name: Highlight templates and hooks changes -on: pull_request +name: Highlight templates changes +on: + pull_request: + paths-ignore: + - '**/changelog/**' + +permissions: {} + jobs: analyze: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} name: Check pull request changes to highlight runs-on: ubuntu-20.04 + permissions: + contents: read outputs: results: ${{ steps.results.outputs.results }} steps: @@ -13,7 +22,7 @@ jobs: run: | npm install -g pnpm npm -g i @wordpress/env@5.1.0 - pnpm install --filter code-analyzer --filter cli-core + pnpm install --filter code-analyzer --filter monorepo-utils - name: Run analyzer id: run working-directory: tools/code-analyzer @@ -21,77 +30,35 @@ jobs: HEAD_REF=$(git rev-parse HEAD) version=$(pnpm run analyzer major-minor "$HEAD_REF" "plugins/woocommerce/woocommerce.php" | tail -n 1) pnpm run analyzer "$HEAD_REF" $version -o "github" - - name: Print results - id: results - run: echo "::set-output name=results::${{ steps.run.outputs.templates }}${{ steps.run.outputs.wphooks }}${{ steps.run.outputs.schema }}${{ steps.run.outputs.database }}" - comment: - name: Add comment to highlight changes - needs: analyze - runs-on: ubuntu-20.04 - steps: - - name: Find Comment - uses: peter-evans/find-comment@v2 - id: find-comment + - name: Check results + uses: actions/github-script@v6 with: - issue-number: ${{ github.event.number }} - body-includes: New hook, template, or database changes in this PR - - name: Add comment - if: ${{ needs.analyze.outputs.results && (steps.find-comment.outputs.comment-id == '') }} - uses: actions/github-script@v5 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: '## New hook, template, or database changes in this PR${{ needs.analyze.outputs.results }}' - }) - - name: Update comment - if: ${{ needs.analyze.outputs.results && steps.find-comment.outputs.comment-id }} - uses: actions/github-script@v5 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.issues.updateComment({ - comment_id: ${{ steps.find-comment.outputs.comment-id }}, - owner: context.repo.owner, - repo: context.repo.repo, - body: '## New hook, template, or database changes in this PR${{ needs.analyze.outputs.results }}' - }) - - name: Delete comment - if: ${{ !needs.analyze.outputs.results && steps.find-comment.outputs.comment-id }} - uses: actions/github-script@v5 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.issues.deleteComment({ - comment_id: ${{ steps.find-comment.outputs.comment-id }}, - owner: context.repo.owner, - repo: context.repo.repo - }) - - name: Add label - if: ${{ needs.analyze.outputs.results }} - uses: actions/github-script@v5 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.issues.addLabels({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - labels: ['release: highlight'] - }) - - name: Remove label - if: ${{ !needs.analyze.outputs.results }} - continue-on-error: true - uses: actions/github-script@v5 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - github.rest.issues.removeLabel({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - name: ['release: highlight'] - }) + const template = '${{ steps.run.outputs.templates }}'; + + if ( template === '' ) { + return; + } + + const templateArr = template.split( '\n' ); + const modTemplateArr = []; + let needsVersionBump = false; + + templateArr.forEach( ( el ) => { + if ( el.match( /NOTICE/ ) ) { + modTemplateArr.pop(); + return; + } + + if ( el.match( /WARNING/ ) ) { + needsVersionBump = true; + } + + modTemplateArr.push( el ); + } ); + + const templateResult = modTemplateArr.join( '\n' ); + + if ( needsVersionBump ) { + core.setFailed( `Templates have changed but template versions were not bumped:\n${ templateResult }` ); + } diff --git a/.github/workflows/pr-lint-monorepo.yml b/.github/workflows/pr-lint-monorepo.yml index 9615dabfe65..b456ecb82ea 100644 --- a/.github/workflows/pr-lint-monorepo.yml +++ b/.github/workflows/pr-lint-monorepo.yml @@ -1,32 +1,38 @@ name: Run lint checks potentially affecting projects across the monorepo on: - pull_request: - branches: - - 'trunk' + pull_request: + branches: + - 'trunk' concurrency: - group: changelogger-${{ github.event_name }}-${{ github.ref }} - cancel-in-progress: true + group: changelogger-${{ github.event_name }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + jobs: - changelogger_used: - name: Changelogger use - runs-on: ubuntu-20.04 - timeout-minutes: 5 - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 0 + changelogger_used: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} + name: Changelogger use + runs-on: ubuntu-20.04 + permissions: + contents: read + timeout-minutes: 15 + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 - - name: Setup WooCommerce Monorepo - uses: ./.github/actions/setup-woocommerce-monorepo - with: - build: false + - name: Setup WooCommerce Monorepo + uses: ./.github/actions/setup-woocommerce-monorepo + with: + build: false - - name: Check change files are touched for touched projects - env: - BASE: ${{ github.event.pull_request.base.sha }} - HEAD: ${{ github.event.pull_request.head.sha }} - run: php tools/monorepo/check-changelogger-use.php --debug "$BASE" "$HEAD" + - name: Check change files are touched for touched projects + env: + BASE: ${{ github.event.pull_request.base.sha }} + HEAD: ${{ github.event.pull_request.head.sha }} + run: php tools/monorepo/check-changelogger-use.php --debug "$BASE" "$HEAD" - - name: Run changelog validation - run: pnpm run -r changelog validate + - name: Run changelog validation + run: pnpm run -r changelog validate diff --git a/.github/workflows/pr-lint-test-js.yml b/.github/workflows/pr-lint-test-js.yml index cc1c14680e8..9cbfcbd3659 100644 --- a/.github/workflows/pr-lint-test-js.yml +++ b/.github/workflows/pr-lint-test-js.yml @@ -1,22 +1,30 @@ name: Lint and tests for JS packages and woocommerce-admin/client -on: pull_request +on: + pull_request: + paths-ignore: + - '**/changelog/**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: lint-test-js: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} name: Lint and Test JS runs-on: ubuntu-20.04 + permissions: + contents: read steps: - uses: actions/checkout@v3 - name: Setup WooCommerce Monorepo - uses: ./.github/actions/setup-woocommerce-monorepo + uses: ./.github/actions/setup-woocommerce-monorepo - name: Lint - run: pnpm run -r --filter='woocommerce/client/admin...' --filter='!@woocommerce/e2e*' --filter='!@woocommerce/api' --color lint + run: pnpm run -r --filter='release-posts' --filter='woocommerce/client/admin...' --filter='!@woocommerce/e2e*' --filter='!@woocommerce/api' --color lint - name: Test - run: pnpm run test --filter='woocommerce/client/admin...' --filter='!@woocommerce/e2e*' --filter='!@woocommerce/api' --color + run: pnpm run test --filter='woocommerce/client/admin...' --filter='!@woocommerce/e2e*' --filter='!@woocommerce/api' --color diff --git a/.github/workflows/pr-project-label.yml b/.github/workflows/pr-project-label.yml index 8c189eace6b..efcbda4b3d6 100644 --- a/.github/workflows/pr-project-label.yml +++ b/.github/workflows/pr-project-label.yml @@ -1,18 +1,23 @@ name: 'Label Pull Request Project' on: - pull_request_target: - types: - - opened - - synchronize -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + pull_request_target: + types: + - opened + - synchronize +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} jobs: - label_project: - runs-on: ubuntu-20.04 - steps: - - uses: actions/labeler@v3 - with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" - configuration-path: .github/project-pr-labeler.yml + label_project: + runs-on: ubuntu-20.04 + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/labeler@v3 + with: + repo-token: '${{ secrets.GITHUB_TOKEN }}' + configuration-path: .github/project-pr-labeler.yml diff --git a/.github/workflows/pr-smoke-test.yml b/.github/workflows/pr-smoke-test.yml index 45e6077c0cf..897433e8936 100644 --- a/.github/workflows/pr-smoke-test.yml +++ b/.github/workflows/pr-smoke-test.yml @@ -1,19 +1,26 @@ name: Run smoke tests against pull request. on: pull_request: + paths-ignore: + - '**/changelog/**' branches: - trunk types: - labeled -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} jobs: prcheck: name: Smoke test a pull request. if: "${{ contains(github.event.label.name, 'run: smoke tests') }}" runs-on: ubuntu-20.04 + permissions: + contents: read + pull-requests: write steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/pr-unit-tests.yml b/.github/workflows/pr-unit-tests.yml index de6e02122d5..cda4e690a7d 100644 --- a/.github/workflows/pr-unit-tests.yml +++ b/.github/workflows/pr-unit-tests.yml @@ -1,69 +1,66 @@ name: Run unit tests on PR -on: - pull_request +on: + pull_request: + paths-ignore: + - '**/changelog/**' defaults: - run: - shell: bash -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + run: + shell: bash +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} jobs: - test: - name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} - timeout-minutes: 30 - runs-on: ubuntu-20.04 - continue-on-error: ${{ matrix.wp == 'nightly' }} - strategy: - fail-fast: false - matrix: - php: [ '7.4', '8.0' ] - wp: [ "latest" ] - include: - - wp: nightly - php: '7.4' - - wp: '5.9' - php: 7.4 - - wp: '5.8' - php: 7.4 - services: - database: - image: mysql:5.6 + test: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} + name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} ${{ matrix.hpos && 'HPOS' || '' }} + timeout-minutes: 30 + runs-on: ubuntu-20.04 + permissions: + contents: read + continue-on-error: ${{ matrix.wp == 'nightly' }} env: - MYSQL_ROOT_PASSWORD: root - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 - steps: - - uses: actions/checkout@v3 + HPOS: ${{ matrix.hpos }} + strategy: + fail-fast: false + matrix: + php: ['7.4', '8.0'] + wp: ['latest'] + include: + - wp: nightly + php: '7.4' + - wp: '6.1' + php: 7.4 + - wp: 'latest' + php: '7.4' + hpos: true + services: + database: + image: mysql:5.6 + env: + MYSQL_ROOT_PASSWORD: root + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 + steps: + - uses: actions/checkout@v3 - - name: Setup WooCommerce Monorepo - uses: ./.github/actions/setup-woocommerce-monorepo - with: - php-version: ${{ matrix.php }} + - name: Setup WooCommerce Monorepo + uses: ./.github/actions/setup-woocommerce-monorepo + with: + php-version: ${{ matrix.php }} - - name: Tool versions - run: | - php --version - composer --version + - name: Tool versions + run: | + php --version + composer --version - - name: Add PHP8 Compatibility. - run: | - if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then - cd plugins/woocommerce - curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip - unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip - composer bin phpunit config --unset platform - composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}' - composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs - rm -rf ./vendor/phpunit/ - composer dump-autoload - fi + - name: Init DB and WP + working-directory: plugins/woocommerce + run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }} - - name: Init DB and WP - working-directory: plugins/woocommerce - run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }} - - - name: Run tests - working-directory: plugins/woocommerce - run: pnpm run test --filter=woocommerce --color + - name: Run tests + working-directory: plugins/woocommerce + run: pnpm run test --filter=woocommerce --color diff --git a/.github/workflows/prepare-package-release.yml b/.github/workflows/prepare-package-release.yml index 9ba5e668979..e230b271ef7 100644 --- a/.github/workflows/prepare-package-release.yml +++ b/.github/workflows/prepare-package-release.yml @@ -3,16 +3,22 @@ on: workflow_dispatch: inputs: packages: - description: 'Enter a specific package to release, or packages separated by commas, ie @woocommerce/components,@woocommerce/number. Leaving this input to the default "-a" will prepare to release all eligible packages.' + description: 'Enter a specific package to release, or packages separated by commas, ie @woocommerce/components,@woocommerce/number. Leaving this input to the default "-a" will prepare to release all eligible packages. When releasing a package for the first time, pass the "--initialRelease" flag.' required: false default: '-a' + +permissions: {} + jobs: prepare: name: Run prepare script runs-on: ubuntu-20.04 + permissions: + contents: write + pull-requests: write steps: - uses: actions/checkout@v3 - + - name: Setup WooCommerce Monorepo uses: ./.github/actions/setup-woocommerce-monorepo @@ -24,17 +30,17 @@ jobs: - name: Get current date id: date - run: echo "::set-output name=date::$(date +'%Y-%m-%d')" + run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT - name: Set all package string id: all_description if: ${{ github.event.inputs.packages == '-a'}} - run: echo "::set-output name=str::all packages" + run: echo "str=all packages" >> $GITHUB_OUTPUT - name: Set Specific packages string id: specific_description if: ${{ github.event.inputs.packages != '-a'}} - run: echo "::set-output name=str::${{ github.event.inputs.packages }}" + run: echo "str=${{ github.event.inputs.packages }}" >> $GITHUB_OUTPUT - name: Create Pull Request uses: peter-evans/create-pull-request@v4 diff --git a/.github/workflows/prime-cache.yml b/.github/workflows/prime-cache.yml index 90a260a6f26..a5d604a1625 100644 --- a/.github/workflows/prime-cache.yml +++ b/.github/workflows/prime-cache.yml @@ -9,10 +9,14 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: prime: name: Prime cache runs-on: ubuntu-20.04 + permissions: + contents: read steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/pull-request-post-merge-processing.yml b/.github/workflows/pull-request-post-merge-processing.yml index 62705adf07f..645d373c8b4 100644 --- a/.github/workflows/pull-request-post-merge-processing.yml +++ b/.github/workflows/pull-request-post-merge-processing.yml @@ -1,44 +1,47 @@ -name: "Pull request post-merge processing" +name: 'Pull request post-merge processing' on: - pull_request_target: - types: [closed] + pull_request_target: + types: [closed] + paths: + - 'packages/**' + - 'plugins/woocommerce/**' + - 'plugins/woocommerce-admin/**' + +permissions: {} jobs: - process-pull-request-after-merge: - name: "Process a pull request after it's merged" - if: github.event.pull_request.merged == true - runs-on: ubuntu-20.04 - steps: - - name: "Get the action scripts" - run: | - scripts="assign-milestone-to-merged-pr.php add-post-merge-comment.php post-request-shared.php" - for script in $scripts - do - curl \ - --silent \ - --fail \ - --header 'Authorization: bearer ${{ secrets.GITHUB_TOKEN }}' \ - --header 'User-Agent: GitHub action to set the milestone for a pull request' \ - --header 'Accept: application/vnd.github.v3.raw' \ - --output $script \ - --location "$GITHUB_API_URL/repos/${{ github.repository }}/contents/.github/workflows/scripts/$script?ref=${{ github.event.pull_request.base.ref }}" - done - env: - GITHUB_API_URL: ${{ env.GITHUB_API_URL }} - - name: "Install PHP" - uses: shivammathur/setup-php@v2 - with: - php-version: '7.4' - - name: "Run the script to assign a milestone" - if: | - !github.event.pull_request.milestone && - github.event.pull_request.base.ref == 'trunk' - run: php assign-milestone-to-merged-pr.php - env: - PULL_REQUEST_ID: ${{ github.event.pull_request.node_id }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: "Run the script to post a comment with next steps hint" - run: php add-post-merge-comment.php - env: - PULL_REQUEST_ID: ${{ github.event.pull_request.node_id }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + process-pull-request-after-merge: + name: "Process a pull request after it's merged" + if: github.event.pull_request.merged == true + runs-on: ubuntu-20.04 + permissions: + pull-requests: write + steps: + - name: 'Get the action scripts' + run: | + scripts="assign-milestone-to-merged-pr.php add-post-merge-comment.php post-request-shared.php" + for script in $scripts + do + curl \ + --silent \ + --fail \ + --header 'Authorization: bearer ${{ secrets.GITHUB_TOKEN }}' \ + --header 'User-Agent: GitHub action to set the milestone for a pull request' \ + --header 'Accept: application/vnd.github.v3.raw' \ + --output $script \ + --location "$GITHUB_API_URL/repos/${{ github.repository }}/contents/.github/workflows/scripts/$script?ref=${{ github.event.pull_request.base.ref }}" + done + env: + GITHUB_API_URL: ${{ env.GITHUB_API_URL }} + - name: 'Install PHP' + uses: shivammathur/setup-php@8e2ac35f639d3e794c1da1f28999385ab6fdf0fc + with: + php-version: '7.4' + - name: 'Run the script to assign a milestone' + if: | + !github.event.pull_request.milestone && + github.event.pull_request.base.ref == 'trunk' + run: php assign-milestone-to-merged-pr.php + env: + PULL_REQUEST_ID: ${{ github.event.pull_request.node_id }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml index bd3514b0947..ab77d208efb 100644 --- a/.github/workflows/release-changelog.yml +++ b/.github/workflows/release-changelog.yml @@ -1,13 +1,13 @@ -name: "Release: Generate changelog" +name: 'Release: Generate changelog' on: - workflow_dispatch: - inputs: - releaseBranch: - description: 'The name of the release branch, in the format `release/x.y`' - required: true - releaseVersion: - description: 'The version of the release, in the format `x.y`' - required: true + workflow_dispatch: + inputs: + releaseBranch: + description: 'The name of the release branch, in the format `release/x.y`' + required: true + releaseVersion: + description: 'The version of the release, in the format `x.y`' + required: true env: GIT_COMMITTER_NAME: 'WooCommerce Bot' @@ -15,97 +15,105 @@ env: GIT_AUTHOR_NAME: 'WooCommerce Bot' GIT_AUTHOR_EMAIL: 'no-reply@woocommerce.com' +permissions: {} + jobs: - create-changelog-prs: - runs-on: ubuntu-20.04 - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - fetch-depth: 0 + create-changelog-prs: + runs-on: ubuntu-20.04 + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 - - name: Setup WooCommerce Monorepo - uses: ./.github/actions/setup-woocommerce-monorepo - with: - build: false + - name: Setup WooCommerce Monorepo + uses: ./.github/actions/setup-woocommerce-monorepo + with: + build: false - - name: "Git fetch the release branch" - run: git fetch origin ${{ inputs.releaseBranch }} - - - name: "Checkout the release branch" - run: git checkout ${{ inputs.releaseBranch }} - - - name: "Create a new branch for the changelog update PR" - run: git checkout -b ${{ format( 'update/{0}-changelog', inputs.releaseVersion ) }} + - name: 'Git fetch the release branch' + run: git fetch origin ${{ inputs.releaseBranch }} - - name: "Generate the changelog file" - run: pnpm --filter=woocommerce run changelog write --add-pr-num -n -vvv --use-version ${{ inputs.releaseVersion }} - - - name: "git rm deleted files" - run: git rm $(git ls-files --deleted) - - - name: "Commit deletion" - run: git commit -m "Delete changelog files from ${{ inputs.releaseVersion }} release" - - - name: "Remember the deletion commit hash" - id: rev-parse - run: echo "::set-output name=hash::$(git rev-parse HEAD)" - - - name: "Insert NEXT_CHANGELOG contents into readme.txt" - run: php .github/workflows/scripts/release-changelog.php - - - name: "git add readme.txt" - run: git add plugins/woocommerce/readme.txt - - - name: "Commit readme" - run: git commit -m "Update the readme files for the ${{ inputs.releaseVersion }} release" - - - name: "Push update branch to origin" - run: git push origin ${{ format( 'update/{0}-changelog', inputs.releaseVersion ) }} - - - name: "Stash any other undesired changes" - run: git stash - - - name: "Checkout trunk" - run: git checkout trunk - - - name: "Create a branch for the changelog files deletion" - run: git checkout -b ${{ format( 'delete/{0}-changelog', inputs.releaseVersion ) }} - - - name: "Cherry-pick the deletion commit" - run: git cherry-pick ${{ steps.rev-parse.outputs.hash }} - - - name: "Push deletion branch to origin" - run: git push origin ${{ format( 'delete/{0}-changelog', inputs.releaseVersion ) }} - - - name: "Create release branch PR" - id: release-pr - uses: actions/github-script@v6 - with: - script: | - const result = await github.rest.pulls.create( { - owner: "${{ github.repository_owner }}", - repo: "${{ github.event.repository.name }}", - head: "${{ format( 'update/{0}-changelog', inputs.releaseVersion ) }}", - base: "${{ inputs.releaseBranch }}", - title: "${{ format( 'Release: Prepare the changelog for {0}', inputs.releaseVersion ) }}", - body: "${{ format( 'This pull request was automatically generated during the code freeze to prepare the changelog for {0}', inputs.releaseVersion ) }}" - } ); - - return result.data.number; - - - name: "Create trunk PR" - id: trunk-pr - uses: actions/github-script@v6 - with: - script: | - const result = await github.rest.pulls.create( { - owner: "${{ github.repository_owner }}", - repo: "${{ github.event.repository.name }}", - head: "${{ format( 'delete/{0}-changelog', inputs.releaseVersion ) }}", - base: "trunk", - title: "${{ format( 'Release: Remove {0} change files', inputs.releaseVersion ) }}", - body: "${{ format( 'This pull request was automatically generated during the code freeze to remove the changefiles from {0} that are compiled into the `{1}` branch via #{2}', inputs.releaseVersion, inputs.releaseBranch, steps.release-pr.outputs.result ) }}" - } ); - - return result.data.number; + - name: 'Checkout the release branch' + run: git checkout ${{ inputs.releaseBranch }} + + - name: 'Create a new branch for the changelog update PR' + run: git checkout -b ${{ format( 'update/{0}-changelog', inputs.releaseVersion ) }} + + - name: 'Generate the changelog file' + run: pnpm --filter=woocommerce run changelog write --add-pr-num -n -vvv --use-version ${{ inputs.releaseVersion }} + + - name: Checkout pnpm-lock.yaml to prevent issues + run: git checkout pnpm-lock.yaml + + - name: 'git rm deleted files' + run: git rm $(git ls-files --deleted) + + - name: 'Commit deletion' + run: git commit -m "Delete changelog files from ${{ inputs.releaseVersion }} release" + + - name: 'Remember the deletion commit hash' + id: rev-parse + run: echo "hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - name: 'Insert NEXT_CHANGELOG contents into readme.txt' + run: php .github/workflows/scripts/release-changelog.php + + - name: 'git add readme.txt' + run: git add plugins/woocommerce/readme.txt + + - name: 'Commit readme' + run: git commit -m "Update the readme files for the ${{ inputs.releaseVersion }} release" + + - name: 'Push update branch to origin' + run: git push origin ${{ format( 'update/{0}-changelog', inputs.releaseVersion ) }} + + - name: 'Stash any other undesired changes' + run: git stash + + - name: 'Checkout trunk' + run: git checkout trunk + + - name: 'Create a branch for the changelog files deletion' + run: git checkout -b ${{ format( 'delete/{0}-changelog', inputs.releaseVersion ) }} + + - name: 'Cherry-pick the deletion commit' + run: git cherry-pick ${{ steps.rev-parse.outputs.hash }} + + - name: 'Push deletion branch to origin' + run: git push origin ${{ format( 'delete/{0}-changelog', inputs.releaseVersion ) }} + + - name: 'Create release branch PR' + id: release-pr + uses: actions/github-script@v6 + with: + script: | + const result = await github.rest.pulls.create( { + owner: "${{ github.repository_owner }}", + repo: "${{ github.event.repository.name }}", + head: "${{ format( 'update/{0}-changelog', inputs.releaseVersion ) }}", + base: "${{ inputs.releaseBranch }}", + title: "${{ format( 'Release: Prepare the changelog for {0}', inputs.releaseVersion ) }}", + body: "${{ format( 'This pull request was automatically generated during the code freeze to prepare the changelog for {0}', inputs.releaseVersion ) }}" + } ); + + return result.data.number; + + - name: 'Create trunk PR' + id: trunk-pr + uses: actions/github-script@v6 + with: + script: | + const result = await github.rest.pulls.create( { + owner: "${{ github.repository_owner }}", + repo: "${{ github.event.repository.name }}", + head: "${{ format( 'delete/{0}-changelog', inputs.releaseVersion ) }}", + base: "trunk", + title: "${{ format( 'Release: Remove {0} change files', inputs.releaseVersion ) }}", + body: "${{ format( 'This pull request was automatically generated during the code freeze to remove the changefiles from {0} that are compiled into the `{1}` branch via #{2}', inputs.releaseVersion, inputs.releaseBranch, steps.release-pr.outputs.result ) }}" + } ); + + return result.data.number; diff --git a/.github/workflows/release-code-freeze.yml b/.github/workflows/release-code-freeze.yml index 4173434e13f..76bca3ca8d8 100644 --- a/.github/workflows/release-code-freeze.yml +++ b/.github/workflows/release-code-freeze.yml @@ -1,162 +1,112 @@ -name: "Release: Code freeze" +name: 'Release: Code freeze' on: - schedule: - - cron: '0 16 * * 4' # Run at 1600 UTC on Thursdays. - workflow_dispatch: - inputs: - timeOverride: - description: "Time Override: The time to use in checking whether the action should run (default: 'now')." - default: 'now' - skipSlackPing: - description: "Skip Slack Ping: If true, the Slack ping will be skipped (useful for testing)" - type: boolean - slackChannelOverride: - description: "Slack Channel Override: The channel ID to send the Slack ping about the freeze" + schedule: + - cron: '0 23 * * 1' # Run at 2300 UTC on Mondays. + workflow_dispatch: + inputs: + timeOverride: + description: "Time Override: The time to use in checking whether the action should run (default: 'now')." + default: 'now' + skipSlackPing: + description: 'Skip Slack Ping: If true, the Slack ping will be skipped (useful for testing)' + type: boolean + slackChannelOverride: + description: 'Slack Channel Override: The channel ID to send the Slack ping about the freeze' env: - TIME_OVERRIDE: ${{ inputs.timeOverride }} + TIME_OVERRIDE: ${{ inputs.timeOverride || 'now' }} GIT_COMMITTER_NAME: 'WooCommerce Bot' GIT_COMMITTER_EMAIL: 'no-reply@woocommerce.com' GIT_AUTHOR_NAME: 'WooCommerce Bot' GIT_AUTHOR_EMAIL: 'no-reply@woocommerce.com' +permissions: {} + jobs: - verify-code-freeze: - name: "Verify that today is the day of the code freeze" - runs-on: ubuntu-20.04 - outputs: - freeze: ${{ steps.check-freeze.outputs.freeze }} - steps: - - name: "Install PHP" - uses: shivammathur/setup-php@v2 - with: - php-version: '7.4' + code-freeze-prep: + name: 'Verify that today is the day of the code freeze and prepare repository' + runs-on: ubuntu-20.04 + permissions: + contents: write + issues: write + pull-requests: write + outputs: + freeze: ${{ steps.check-freeze.outputs.freeze }} + nextReleaseBranch: ${{ steps.branch.outputs.nextReleaseBranch }} + nextReleaseVersion: ${{ steps.milestone.outputs.nextReleaseVersion }} + nextDevelopmentVersion: ${{ steps.milestone.outputs.nextDevelopmentVersion }} + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 - - name: "Check whether today is the code freeze day" - id: check-freeze - shell: php {0} - run: | - 14 ) { - echo '::set-output name=freeze::1'; - } else { - echo '::set-output name=freeze::0'; - } + - name: Create next milestone + id: milestone + if: steps.check-freeze.outputs.freeze == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: pnpm run utils code-freeze milestone -o ${{ github.repository_owner }} - maybe-create-next-milestone-and-release-branch: - name: "Maybe create next milestone and release branch" - runs-on: ubuntu-20.04 - needs: verify-code-freeze - if: needs.verify-code-freeze.outputs.freeze == 0 - outputs: - branch: ${{ steps.freeze.outputs.branch }} - release_version: ${{ steps.freeze.outputs.release_version }} - next_version: ${{ steps.freeze.outputs.next_version }} - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - fetch-depth: 100 + - name: Create next release branch + id: branch + if: steps.check-freeze.outputs.freeze == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: pnpm run utils code-freeze branch -o ${{ github.repository_owner }} - - name: Setup WooCommerce Monorepo - uses: ./.github/actions/setup-woocommerce-monorepo - with: - build: false + - name: Prepare trunk for next development cycle + id: prep-trunk + if: steps.check-freeze.outputs.freeze == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: pnpm run utils code-freeze version-bump -o ${{ github.repository_owner }} -v ${{ steps.milestone.outputs.nextDevelopmentVersion }}.0-dev - - name: "Run the script to enforce the code freeze" - id: freeze - run: php .github/workflows/scripts/release-code-freeze.php - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_OUTPUTS: 1 + notify-slack: + name: 'Sends code freeze notification to Slack' + runs-on: ubuntu-20.04 + needs: code-freeze-prep + if: ${{ inputs.skipSlackPing != true }} + steps: + - name: Slack + uses: archive/github-actions-slack@v2.0.0 + id: notify + with: + slack-bot-user-oauth-access-token: ${{ secrets.CODE_FREEZE_BOT_TOKEN }} + slack-channel: ${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }} + slack-text: | + :warning-8c: ${{ needs.code-freeze-prep.outputs.nextReleaseVersion }} Code Freeze :ice_cube: - prep-trunk: - name: Preps trunk for next development cycle - runs-on: ubuntu-20.04 - needs: maybe-create-next-milestone-and-release-branch - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - fetch-depth: 100 + The automation to cut the release branch for ${{ needs.code-freeze-prep.outputs.nextReleaseVersion }} has run. Any PRs that were not already merged will be a part of ${{ needs.maybe-create-next-milestone-and-release-branch.outputs.nextDevelopmentVersion }} by default. If you have something that needs to make ${{ needs.maybe-create-next-milestone-and-release-branch.outputs.nextReleaseVersion }} that hasn't yet been merged, please see the <${{ secrets.FG_LINK }}/code-freeze-for-woocommerce-core-release/|fieldguide page for the code freeze>. - - name: fetch-trunk - run: git fetch origin trunk - - - name: checkout-trunk - run: git checkout trunk - - - name: Setup WooCommerce Monorepo - uses: ./.github/actions/setup-woocommerce-monorepo - - - name: Create branch - run: git checkout -b prep/trunk-for-next-dev-cycle-${{ needs.maybe-create-next-milestone-and-release-branch.outputs.next_version }} - - - name: Bump versions - working-directory: ./tools/version-bump - run: pnpm run version bump woocommerce -v ${{ needs.maybe-create-next-milestone-and-release-branch.outputs.next_version }}.0-dev - - - name: Checkout pnpm-lock.yaml to prevent issues - run: git checkout pnpm-lock.yaml - - - name: Commit changes - run: git commit -am "Prep trunk for ${{ needs.maybe-create-next-milestone-and-release-branch.outputs.next_version }} cycle" - - - name: Push branch up - run: git push --no-verify origin prep/trunk-for-next-dev-cycle-${{ needs.maybe-create-next-milestone-and-release-branch.outputs.next_version }} - - - name: Create the PR - uses: actions/github-script@v6 - with: - script: | - const body = "This PR updates the versions in trunk to ${{ needs.maybe-create-next-milestone-and-release-branch.outputs.next_version }} for next development cycle." - - const pr = await github.rest.pulls.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: "Prep trunk for ${{ needs.maybe-create-next-milestone-and-release-branch.outputs.next_version }} cycle", - head: "prep/trunk-for-next-dev-cycle-${{ needs.maybe-create-next-milestone-and-release-branch.outputs.next_version }}", - base: "trunk", - body: body - }) - - notify-slack: - name: "Sends code freeze notification to Slack" - if: ${{ inputs.skipSlackPing != true }} - runs-on: ubuntu-20.04 - needs: maybe-create-next-milestone-and-release-branch - steps: - - name: Slack - uses: archive/github-actions-slack@v2.0.0 - id: notify - with: - slack-bot-user-oauth-access-token: ${{ secrets.CODE_FREEZE_BOT_TOKEN }} - slack-channel: ${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }} - slack-text: | - :warning-8c: ${{ needs.maybe-create-next-milestone-and-release-branch.outputs.release_version }} Code Freeze :ice_cube: - - The automation to cut the release branch for ${{ needs.maybe-create-next-milestone-and-release-branch.outputs.release_version }} has run. Any PRs that were not already merged will be a part of ${{ needs.maybe-create-next-milestone-and-release-branch.outputs.next_version }} by default. If you have something that needs to make ${{ needs.maybe-create-next-milestone-and-release-branch.outputs.release_version }} that hasn't yet been merged, please see the <${{ secrets.FG_LINK }}/code-freeze-for-woocommerce-core-release/|fieldguide page for the code freeze>. - - trigger-changelog-action: - name: "Trigger changelog action" - runs-on: ubuntu-20.04 - needs: maybe-create-next-milestone-and-release-branch - steps: - - run: | - curl \ - -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: token ${{ secrets.WC_BOT_TRIAGE_TOKEN }}" \ - -d '{"ref":"refs/heads/trunk","inputs":{"releaseBranch":"${{ needs.maybe-create-next-milestone-and-release-branch.outputs.branch }}","releaseVersion":"${{ needs.maybe-create-next-milestone-and-release-branch.outputs.release_version }}"}}' \ - https://api.github.com/repos/${{ github.repository }}/actions/workflows/release-changelog.yml/dispatches + trigger-changelog-action: + name: 'Trigger changelog action' + runs-on: ubuntu-20.04 + permissions: + actions: write + needs: code-freeze-prep + if: needs.code-freeze-prep.outputs.freeze == 'true' + steps: + - name: 'Trigger changelog action' + uses: actions/github-script@v6 + with: + script: | + github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'release-changelog.yml', + ref: 'trunk', + inputs: { + releaseVersion: "${{ needs.code-freeze-prep.outputs.nextReleaseVersion }}", + releaseBranch: "${{ needs.code-freeze-prep.outputs.nextReleaseBranch }}" + } + }) diff --git a/.github/workflows/release-wc-beta-tester.yml b/.github/workflows/release-wc-beta-tester.yml new file mode 100644 index 00000000000..c2e6c7adf45 --- /dev/null +++ b/.github/workflows/release-wc-beta-tester.yml @@ -0,0 +1,34 @@ +name: WooCommerce Beta Tester Release +permissions: {} + +on: + workflow_dispatch: + inputs: + version: + description: 'The version number for the release' + required: true + +jobs: + release: + name: Run release scripts + runs-on: ubuntu-20.04 + permissions: + contents: write + steps: + - uses: actions/checkout@v3 + + - name: Setup WooCommerce Monorepo + uses: ./.github/actions/setup-woocommerce-monorepo + + - name: Build WooCommerce Beta Tester Zip + working-directory: plugins/woocommerce-beta-tester + run: pnpm build:zip + + - name: Create release + id: create_release + uses: woocommerce/action-gh-release@master + with: + tag_name: wc-beta-tester-${{ inputs.version }} + name: WooCommerce Beta Tester Release ${{ inputs.version }} + draft: false + files: plugins/woocommerce-beta-tester/woocommerce-beta-tester.zip diff --git a/.github/workflows/review-testing-instructions.yml b/.github/workflows/review-testing-instructions.yml new file mode 100644 index 00000000000..083db641253 --- /dev/null +++ b/.github/workflows/review-testing-instructions.yml @@ -0,0 +1,66 @@ +name: Remind reviewers to also review the testing instructions. +on: + pull_request_target: + types: [review_requested] + +permissions: {} + +jobs: + add-testing-instructions-review-comment: + runs-on: ubuntu-20.04 + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c + + - name: Install Octokit + run: npm --prefix .github/workflows/scripts install @octokit/action + + - name: Install Actions Core + run: npm --prefix .github/workflows/scripts install @actions/core + + - name: Check if user is a community contributor + id: is-community-contributor + run: node .github/workflows/scripts/is-community-contributor.js + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get the username of requested reviewers + if: steps.is-community-contributor.outputs.is-community == 'no' + 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 diff --git a/.github/workflows/scripts/get-plugin-version.js b/.github/workflows/scripts/get-plugin-version.js new file mode 100644 index 00000000000..4b2f0b9c063 --- /dev/null +++ b/.github/workflows/scripts/get-plugin-version.js @@ -0,0 +1,13 @@ +const { readFile } = require( 'fs/promises' ); +const { join } = require( 'path' ); + +exports.getVersion = async plugin => { + const filePath = join( + process.env.GITHUB_WORKSPACE, + `plugins/${ plugin }/${ plugin }.php` + ); + const pluginFileContents = await readFile( filePath, 'utf8' ); + const versionMatch = pluginFileContents.match( /Version: (\d+\.\d+\.\d+.*)\n/m ); + return versionMatch && versionMatch[1]; +}; + diff --git a/.github/workflows/scripts/is-community-contributor.js b/.github/workflows/scripts/is-community-contributor.js index 7708995fc06..9a4311d7878 100644 --- a/.github/workflows/scripts/is-community-contributor.js +++ b/.github/workflows/scripts/is-community-contributor.js @@ -1,50 +1,62 @@ -// Note you'll need to install this dependency as part of your workflow. -const { Octokit } = require('@octokit/action'); +// Note you'll need to install these dependencies as part of your workflow. +const { Octokit } = require( '@octokit/action' ); +const core = require( '@actions/core' ); // Note that this script assumes you set GITHUB_TOKEN in env, if you don't // this won't work. const octokit = new Octokit(); -const getIssueAuthor = (payload) => { - return payload?.issue?.user?.login || payload?.pull_request?.user?.login || null; -} +const getIssueAuthor = ( payload ) => { + return ( + payload?.issue?.user?.login || + payload?.pull_request?.user?.login || + null + ); +}; -const isCommunityContributor = async (owner, repo, username) => { - if (username) { - const {data: {permission}} = await octokit.rest.repos.getCollaboratorPermissionLevel({ +const isCommunityContributor = async ( owner, repo, username ) => { + if ( username ) { + const { + data: { permission }, + } = await octokit.rest.repos.getCollaboratorPermissionLevel( { owner, repo, username, - }); - + } ); + return permission === 'read' || permission === 'none'; } - return false; -} + return false; +}; -const addLabel = async(label, owner, repo, issueNumber) => { - await octokit.rest.issues.addLabels({ +const addLabel = async ( label, owner, repo, issueNumber ) => { + await octokit.rest.issues.addLabels( { owner, repo, issue_number: issueNumber, - labels: [label], - }); -} + labels: [ label ], + } ); +}; const applyLabelToCommunityContributor = async () => { - const eventPayload = require(process.env.GITHUB_EVENT_PATH); - const username = getIssueAuthor(eventPayload); - const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/"); + const eventPayload = require( process.env.GITHUB_EVENT_PATH ); + const username = getIssueAuthor( eventPayload ); + const [ owner, repo ] = process.env.GITHUB_REPOSITORY.split( '/' ); const { number } = eventPayload?.issue || eventPayload?.pull_request; - - const isCommunityUser = await isCommunityContributor(owner, repo, username); - console.log( '::set-output name=is-community::%s', isCommunityUser ? 'yes' : 'no' ); - - if (isCommunityUser) { - console.log('Adding community contributor label'); - await addLabel('type: community contribution', owner, repo, number); + + const isCommunityUser = await isCommunityContributor( + owner, + repo, + username + ); + + core.setOutput( 'is-community', isCommunityUser ? 'yes' : 'no' ); + + if ( isCommunityUser ) { + console.log( 'Adding community contributor label' ); + await addLabel( 'type: community contribution', owner, repo, number ); } -} +}; applyLabelToCommunityContributor(); diff --git a/.github/workflows/scripts/prepare-test-summary-daily.js b/.github/workflows/scripts/prepare-test-summary-daily.js index 784e42bf396..7c9ae2c90b1 100644 --- a/.github/workflows/scripts/prepare-test-summary-daily.js +++ b/.github/workflows/scripts/prepare-test-summary-daily.js @@ -92,7 +92,7 @@ const addSummaryHeadingAndTable = ( core ) => { const apiTableRow = createAPITableRow(); const e2eTableRow = createE2ETableRow(); - core.summary.addHeading( 'Smoke tests on trunk' ).addTable( [ + core.summary.addHeading( 'Smoke tests on nightly build' ).addTable( [ [ { data: 'Test :test_tube:', header: true }, { data: 'Passed :white_check_mark:', header: true }, diff --git a/.github/workflows/scripts/release-changelog.php b/.github/workflows/scripts/release-changelog.php index c66f641140d..3dfad7c7480 100644 --- a/.github/workflows/scripts/release-changelog.php +++ b/.github/workflows/scripts/release-changelog.php @@ -10,8 +10,8 @@ if ( getenv( 'TIME_OVERRIDE' ) ) { $base_dir = dirname( dirname( dirname( __DIR__ ) ) ); -// The release date is 26 days after the code freeze. -$release_time = strtotime( '+26 days', $now ); +// The release date is 22 days after the code freeze. +$release_time = strtotime( '+22 days', $now ); $release_date = date( 'Y-m-d', $release_time ); $readme_file = $base_dir . '/plugins/woocommerce/readme.txt'; diff --git a/.github/workflows/scripts/release-code-freeze.php b/.github/workflows/scripts/release-code-freeze.php deleted file mode 100644 index cc0ebe197c7..00000000000 --- a/.github/workflows/scripts/release-code-freeze.php +++ /dev/null @@ -1,75 +0,0 @@ - 14 ) { - echo 'Info: Today is not the Thursday of the code freeze.' . PHP_EOL; - exit( 1 ); -} - -$latest_version_with_release = get_latest_version_with_release(); - -if ( empty( $latest_version_with_release ) ) { - echo '*** Error: Unable to get latest version with release' . PHP_EOL; - exit( 1 ); -} - -// Because we go from 5.9 to 6.0, we can get the next major_minor by adding 0.1 and formatting appropriately. -$latest_float = (float) $latest_version_with_release; -$branch_major_minor = number_format( $latest_float + 0.1, 1 ); -$milestone_major_minor = number_format( $latest_float + 0.2, 1 ); - -// We use those values to get the release branch and next milestones that we need to create. -$release_branch_to_create = "release/{$branch_major_minor}"; -$milestone_to_create = "{$milestone_major_minor}.0"; - -if ( getenv( 'GITHUB_OUTPUTS' ) ) { - echo 'Including GitHub Outputs...' . PHP_EOL; - echo '::set-output name=next_version::' . $milestone_major_minor . PHP_EOL; - echo '::set-output name=release_version::' . $branch_major_minor . PHP_EOL; - echo '::set-output name=branch::' . $release_branch_to_create . PHP_EOL; - echo '::set-output name=milestone::' . $milestone_to_create . PHP_EOL; -} - -if ( getenv( 'DRY_RUN' ) ) { - echo 'DRY RUN: Skipping actual creation of release branch and milestone...' . PHP_EOL; - echo "Release Branch: {$release_branch_to_create}" . PHP_EOL; - echo "Milestone: {$milestone_to_create}" . PHP_EOL; - return; -} - -if ( create_github_milestone( $milestone_to_create ) ) { - echo "Created milestone {$milestone_to_create}" . PHP_EOL; -} else if ( '422' === $github_api_response_code ) { - // The milestone already existed when GitHub returns a 422 status. - echo "Notice: Unable to create {$milestone_to_create} milestone. Maybe it already exists? Skipping..." . PHP_EOL; -} else { - echo "*** Error: Unable to create {$milestone_to_create} milestone" . PHP_EOL; -} - -if ( create_github_branch_from_branch( 'trunk', $release_branch_to_create ) ) { - echo "Created branch {$release_branch_to_create}" . PHP_EOL; -} else if ( '422' === $github_api_response_code ) { - // The release branch already existed when GitHub returns a 422 status. - echo "Notice: Unable to create {$release_branch_to_create} branch. Maybe it already exists? Skipping..." . PHP_EOL; - exit( 1 ); -} else { - echo "*** Error: Unable to create {$release_branch_to_create}" . PHP_EOL; - exit( 1 ); -} diff --git a/.github/workflows/scripts/verify-php-version.sh b/.github/workflows/scripts/verify-php-version.sh new file mode 100644 index 00000000000..df888962966 --- /dev/null +++ b/.github/workflows/scripts/verify-php-version.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +# +# Verify that the PHP version in the launched WP ENV environment is equal to expected. +# + +cd $GITHUB_WORKSPACE/plugins/woocommerce +ACTUAL_PHP_VERSION=$(pnpm exec wp-env run tests-cli "wp --info | grep 'PHP version:'") +EXIT_CODE='' + +echo "PHP version found in WP Env environment: \"$ACTUAL_PHP_VERSION\"" +echo "Expected PHP version: \"$EXPECTED_PHP_VERSION\"" + +if [[ $ACTUAL_PHP_VERSION == *"$EXPECTED_PHP_VERSION"* ]] + then + EXIT_CODE=0 + else + EXIT_CODE=1 +fi + +exit $EXIT_CODE diff --git a/.github/workflows/smoke-test-daily-site-check.yml b/.github/workflows/smoke-test-daily-site-check.yml index ac71633d3b1..37b0de06ec9 100644 --- a/.github/workflows/smoke-test-daily-site-check.yml +++ b/.github/workflows/smoke-test-daily-site-check.yml @@ -3,6 +3,8 @@ on: schedule: - cron: '25 7 * * *' +permissions: {} + jobs: ping_site: runs-on: ubuntu-20.04 diff --git a/.github/workflows/smoke-test-daily.yml b/.github/workflows/smoke-test-daily.yml index 640cd61cd27..e91f8482ed0 100644 --- a/.github/workflows/smoke-test-daily.yml +++ b/.github/workflows/smoke-test-daily.yml @@ -1,25 +1,29 @@ name: Smoke test daily on: - # schedule: - # - cron: '25 3 * * *' + schedule: + - cron: '25 3 * * *' workflow_dispatch: env: API_ARTIFACT: api-daily--run-${{ github.run_number }} E2E_ARTIFACT: e2e-daily--run-${{ github.run_number }} FORCE_COLOR: 1 - BRANCH_NAME: ${{ github.ref_name }} concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + jobs: - e2e-tests: - name: E2E tests on trunk + api-tests: + name: API tests on nightly build runs-on: ubuntu-20.04 - if: always() + 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 BASE_URL: ${{ secrets.SMOKE_TEST_URL }} ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }} ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }} @@ -29,8 +33,6 @@ jobs: DEFAULT_TIMEOUT_OVERRIDE: 120000 steps: - uses: actions/checkout@v3 - with: - ref: ${{ env.BRANCH_NAME }} - name: Setup WooCommerce Monorepo uses: ./.github/actions/setup-woocommerce-monorepo @@ -44,90 +46,24 @@ jobs: - name: Run 'Update WooCommerce' test. working-directory: plugins/woocommerce - id: e2e-update env: - UPDATE_WC: true - run: pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js update-woocommerce.spec.js - - - name: Run the rest of E2E tests. - timeout-minutes: 60 - working-directory: plugins/woocommerce - id: e2e - env: - E2E_MAX_FAILURES: 15 - run: pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js basic.spec.js - - - name: Generate Playwright E2E Test report. - id: generate_e2e_report - if: | - always() && - ( - steps.e2e-update.conclusion != 'cancelled' || - steps.e2e-update.conclusion != 'skipped' || - steps.e2e.conclusion != 'cancelled' || - steps.e2e.conclusion != 'skipped' - ) - working-directory: plugins/woocommerce - run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }} - - - name: Archive E2E test report - if: | - always() && - steps.generate_e2e_report.conclusion == 'success' - uses: actions/upload-artifact@v3 - with: - name: ${{ env.E2E_ARTIFACT }} - path: | - ${{ env.ALLURE_RESULTS_DIR }} - ${{ env.ALLURE_REPORT_DIR }} - if-no-files-found: ignore - retention-days: 5 - - api-tests: - name: API tests on trunk - runs-on: ubuntu-20.04 - needs: [e2e-tests] - if: always() - env: - ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/api-test-report/allure-results - ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/api-test-report/allure-report - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ env.BRANCH_NAME }} - - - name: Setup WooCommerce Monorepo - uses: ./.github/actions/setup-woocommerce-monorepo - with: - install-filters: woocommerce - build: false + UPDATE_WC: nightly + run: pnpm exec playwright test --config=tests/e2e-pw/daily.playwright.config.js update-woocommerce.spec.js - name: Run API tests. - if: always() - id: run_playwright_api_tests working-directory: plugins/woocommerce env: - BASE_URL: ${{ secrets.SMOKE_TEST_URL }} USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }} USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }} - DEFAULT_TIMEOUT_OVERRIDE: 120000 run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello.test.js - name: Generate API Test report. - id: generate_api_report - if: | - always() && - ( - steps.run_playwright_api_tests.conclusion != 'cancelled' || - steps.run_playwright_api_tests.conclusion != 'skipped' - ) + if: success() || failure() working-directory: plugins/woocommerce run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }} - name: Archive API test report - if: | - always() && - steps.generate_api_report.conclusion == 'success' + if: success() || failure() uses: actions/upload-artifact@v3 with: name: ${{ env.API_ARTIFACT }} @@ -137,15 +73,69 @@ jobs: if-no-files-found: ignore retention-days: 5 - k6-tests: - name: k6 tests on trunk + e2e-tests: + name: E2E tests on nightly build runs-on: ubuntu-20.04 + permissions: + contents: read needs: [api-tests] - if: always() + env: + ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }} + ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }} + ADMIN_USER_EMAIL: ${{ secrets.SMOKE_TEST_ADMIN_USER_EMAIL }} + ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-report + ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-results + BASE_URL: ${{ secrets.SMOKE_TEST_URL }} + CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_CUSTOMER_PASSWORD }} + CUSTOMER_USER: ${{ secrets.SMOKE_TEST_CUSTOMER_USER }} + DEFAULT_TIMEOUT_OVERRIDE: 120000 + steps: - uses: actions/checkout@v3 + + - name: Setup WooCommerce Monorepo + uses: ./.github/actions/setup-woocommerce-monorepo with: - ref: ${{ env.BRANCH_NAME }} + install-filters: woocommerce + build: false + + - name: Download and install Chromium browser. + working-directory: plugins/woocommerce + run: pnpm exec playwright install chromium + + - name: Run E2E tests. + timeout-minutes: 60 + working-directory: plugins/woocommerce + env: + E2E_MAX_FAILURES: 25 + RESET_SITE: true + run: pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js + + - name: Generate Playwright E2E Test report. + if: success() || failure() + working-directory: plugins/woocommerce + run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }} + + - name: Archive E2E test report + if: success() || failure() + uses: actions/upload-artifact@v3 + with: + name: ${{ env.E2E_ARTIFACT }} + path: | + ${{ env.ALLURE_RESULTS_DIR }} + ${{ env.ALLURE_REPORT_DIR }} + if-no-files-found: ignore + retention-days: 5 + + k6-tests: + name: k6 tests on nightly build + runs-on: ubuntu-20.04 + permissions: + contents: read + needs: [api-tests] + if: success() || failure() + steps: + - uses: actions/checkout@v3 - name: Setup WooCommerce Monorepo uses: ./.github/actions/setup-woocommerce-monorepo @@ -158,7 +148,6 @@ jobs: run: pnpm exec playwright install chromium - name: Update performance test site with E2E test - if: always() working-directory: plugins/woocommerce env: BASE_URL: ${{ secrets.SMOKE_TEST_PERF_URL }}/ @@ -166,19 +155,17 @@ jobs: ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_PERF_ADMIN_PASSWORD }} CUSTOMER_USER: ${{ secrets.SMOKE_TEST_PERF_ADMIN_USER }} CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_PERF_ADMIN_PASSWORD }} - UPDATE_WC: true + UPDATE_WC: nightly DEFAULT_TIMEOUT_OVERRIDE: 120000 run: | pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js update-woocommerce.spec.js continue-on-error: true - name: Install k6 - if: always() 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 smoke tests - if: always() env: URL: ${{ secrets.SMOKE_TEST_PERF_URL }} HOST: ${{ secrets.SMOKE_TEST_PERF_HOST }} @@ -186,15 +173,16 @@ jobs: A_PW: ${{ secrets.SMOKE_TEST_PERF_ADMIN_PASSWORD }} C_USER: ${{ secrets.SMOKE_TEST_PERF_ADMIN_USER }} C_PW: ${{ secrets.SMOKE_TEST_PERF_ADMIN_PASSWORD }} - P_ID: 274 + P_ID: 22733 run: | ./k6 run plugins/woocommerce/tests/performance/tests/gh-action-daily-ext-requests.js test-plugins: - name: Smoke tests with ${{ matrix.plugin }} plugin installed + name: Smoke tests on trunk with ${{ matrix.plugin }} plugin installed runs-on: ubuntu-20.04 - needs: [k6-tests] - if: always() + permissions: + contents: read + needs: [api-tests] env: USE_WP_ENV: 1 ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/allure-results @@ -218,8 +206,6 @@ jobs: repo: 'takayukister/contact-form-7' steps: - uses: actions/checkout@v3 - with: - ref: ${{ env.BRANCH_NAME }} - name: Setup WooCommerce Monorepo uses: ./.github/actions/setup-woocommerce-monorepo @@ -233,7 +219,6 @@ jobs: run: pnpm exec playwright install chromium - name: Run 'Upload plugin' test - id: e2e-upload working-directory: plugins/woocommerce env: PLUGIN_REPOSITORY: ${{ matrix.private && secrets[matrix.repo] || matrix.repo }} @@ -242,32 +227,21 @@ jobs: run: pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js upload-plugin.spec.js - name: Run the rest of E2E tests - id: e2e working-directory: plugins/woocommerce env: E2E_MAX_FAILURES: 15 - run: pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js basic.spec.js + run: pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js - name: Generate E2E Test report. - id: report - if: | - always() && - ( - steps.e2e-upload.conclusion != 'cancelled' || - steps.e2e-upload.conclusion != 'skipped' || - steps.e2e.conclusion != 'cancelled' || - steps.e2e.conclusion != 'skipped' - ) + if: success() || failure() working-directory: plugins/woocommerce run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }} - name: Archive E2E test report - if: | - always() && - steps.report.conclusion == 'success' + if: success() || failure() uses: actions/upload-artifact@v3 with: - name: Smoke tests with ${{ matrix.plugin }} plugin installed (run ${{ github.run_number }}) + name: Smoke tests on trunk with ${{ matrix.plugin }} plugin installed (run ${{ github.run_number }}) path: | ${{ env.ALLURE_RESULTS_DIR }} ${{ env.ALLURE_REPORT_DIR }} @@ -275,16 +249,19 @@ jobs: retention-days: 5 trunk-results: - name: Publish report on smoke tests on trunk - if: always() && + name: Publish report on smoke tests on nightly build + if: | + ( success() || failure() ) && ! github.event.pull_request.head.repo.fork runs-on: ubuntu-20.04 - needs: [test-plugins] + permissions: + contents: read + needs: [e2e-tests, test-plugins, k6-tests] steps: - name: Create dirs run: | mkdir -p repo - mkdir -p artifacts/api + mkdir -p artifacts/api mkdir -p artifacts/e2e mkdir -p output @@ -292,7 +269,6 @@ jobs: uses: actions/checkout@v3 with: path: repo - ref: ${{ env.BRANCH_NAME }} - name: Download API test report artifact uses: actions/download-artifact@v3 @@ -330,16 +306,16 @@ jobs: --repo woocommerce/woocommerce-test-reports plugins-results: - name: Publish report on smoke tests with plugins + name: Publish report on Smoke tests on trunk with plugins if: | - always() && + ( success() || failure() ) && ! github.event.pull_request.head.repo.fork runs-on: ubuntu-20.04 - needs: [test-plugins] + needs: [e2e-tests, test-plugins, k6-tests] env: GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }} RUN_ID: ${{ github.run_id }} - ARTIFACT: Smoke tests with ${{ matrix.plugin }} plugin installed (run ${{ github.run_number }}) + ARTIFACT: Smoke tests on trunk with ${{ matrix.plugin }} plugin installed (run ${{ github.run_number }}) strategy: fail-fast: false matrix: diff --git a/.github/workflows/smoke-test-pr-merge.yml b/.github/workflows/smoke-test-pr-merge.yml new file mode 100644 index 00000000000..60720ea8454 --- /dev/null +++ b/.github/workflows/smoke-test-pr-merge.yml @@ -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 diff --git a/.github/workflows/smoke-test-release.yml b/.github/workflows/smoke-test-release.yml index f202b3467de..bc07de36ba7 100644 --- a/.github/workflows/smoke-test-release.yml +++ b/.github/workflows/smoke-test-release.yml @@ -1,176 +1,658 @@ name: Smoke test release on: + release: + types: [released, prereleased, published] workflow_dispatch: inputs: - release_id: - description: 'WooCommerce Release Id' + tag: + description: 'WooCommerce Release Tag' required: true +concurrency: + group: ${{ github.workflow }}-${{ github.event.release.tag_name || inputs.tag }} + cancel-in-progress: true +permissions: {} +env: + 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: - login-run: - name: Daily smoke test on release. + get-tag: + name: Get WooCommerce release tag + permissions: + contents: read runs-on: ubuntu-20.04 + outputs: + tag: ${{ steps.get-tag.outputs.tag }} + created: ${{ steps.created-at.outputs.created }} + steps: + - name: Validate tag + if: ${{ github.event_name == 'workflow_dispatch' }} + env: + GH_TOKEN: ${{ secrets.E2E_GH_TOKEN }} + run: gh release view "${{ inputs.tag }}" --repo=woocommerce/woocommerce + + - name: Get tag from triggered event + id: get-tag + env: + RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }} + run: | + echo "Triggered event: ${{ github.event_name }}" + echo "Tag from event: $RELEASE_TAG" + echo "tag=$RELEASE_TAG" >> $GITHUB_OUTPUT + + - name: Verify woocommerce.zip asset + env: + GH_TOKEN: ${{ secrets.E2E_GH_TOKEN }} + RELEASE_TAG: ${{ steps.get-tag.outputs.tag }} + run: | + ASSET_NAMES=$(gh release view $RELEASE_TAG --repo woocommerce/woocommerce --json assets --jq ".assets[].name") + if [[ $ASSET_NAMES == *"woocommerce.zip"* ]] + then + echo "$RELEASE_TAG has a valid woocommerce.zip asset." + exit 0 + fi + + echo "$RELEASE_TAG does not have a valid woocommerce.zip asset." + exit 1 + + - name: Get 'created-at' of WooCommerce zip + id: created-at + env: + GH_TOKEN: ${{ secrets.E2E_GH_TOKEN }} + run: echo "created=$(gh release view ${{ steps.get-tag.outputs.tag }} --json assets --jq .assets[0].createdAt --repo woocommerce/woocommerce)" >> $GITHUB_OUTPUT + + e2e-update-wc: + name: Test WooCommerce update + runs-on: ubuntu-20.04 + needs: [get-tag] + permissions: + contents: read + env: + ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-report + ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-results steps: - uses: actions/checkout@v3 - with: - ref: trunk - name: Setup WooCommerce Monorepo uses: ./.github/actions/setup-woocommerce-monorepo with: - build-filters: woocommerce + install-filters: woocommerce + build: false - - name: Install Jest - run: npm install -g jest - - - name: Run smoke 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: - SMOKE_TEST_URL: ${{ secrets.RELEASE_TEST_URL }} - SMOKE_TEST_ADMIN_USER: ${{ secrets.RELEASE_TEST_ADMIN_USER }} - SMOKE_TEST_ADMIN_PASSWORD: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }} - SMOKE_TEST_ADMIN_USER_EMAIL: ${{ secrets.RELEASE_TEST_ADMIN_USER_EMAIL }} - SMOKE_TEST_CUSTOMER_USER: ${{ secrets.RELEASE_TEST_CUSTOMER_USER }} - SMOKE_TEST_CUSTOMER_PASSWORD: ${{ secrets.RELEASE_TEST_CUSTOMER_PASSWORD }} - WC_E2E_SCREENSHOTS: 1 - E2E_RETEST: 1 - E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }} - E2E_SLACK_CHANNEL: 'C02DS4NE72S' - TEST_RELEASE: 1 - UPDATE_WC: 1 + ADMIN_PASSWORD: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }} + ADMIN_USER: ${{ secrets.RELEASE_TEST_ADMIN_USER }} + BASE_URL: ${{ secrets.RELEASE_TEST_URL }} + CUSTOMER_PASSWORD: ${{ secrets.RELEASE_TEST_CUSTOMER_PASSWORD }} + CUSTOMER_USER: ${{ secrets.RELEASE_TEST_CUSTOMER_USER }} DEFAULT_TIMEOUT_OVERRIDE: 120000 + GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }} + UPDATE_WC: ${{ needs.get-tag.outputs.tag }} + + - 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() && steps.run-e2e-composite-action.conclusion == 'failure' ) + env: + GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }} + ENV_DESCRIPTION: wp-latest + run: | + gh workflow run publish-test-reports-release.yml \ + -f created_at="${{ needs.get-tag.outputs.created }}" \ + -f run_id=${{ github.run_id }} \ + -f run_number=${{ github.run_number }} \ + -f release_tag=${{ needs.get-tag.outputs.tag }} \ + -f artifact="${{ env.E2E_WP_LATEST_ARTIFACT }}" \ + -f env_description="${{ env.ENV_DESCRIPTION }}" \ + -f test_type="e2e" \ + --repo woocommerce/woocommerce-test-reports + + api-wp-latest: + name: API on WP Latest + runs-on: ubuntu-20.04 + needs: [get-tag, e2e-update-wc] + permissions: + contents: read + env: + ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/api-test-report/allure-report + ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/api-test-report/allure-results + API_WP_LATEST_ARTIFACT: API test on release smoke test site with WP Latest (run ${{ github.run_number }}) + steps: + - uses: actions/checkout@v3 + + - name: Setup WooCommerce Monorepo + uses: ./.github/actions/setup-woocommerce-monorepo + with: + install-filters: woocommerce + build: false + + - name: Run API tests + 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 wc-e2e docker:up - pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js - pnpm exec wc-e2e test:e2e - pnpm exec wc-api-tests test api - test-wp-version: - name: Smoke test on L-${{ matrix.wp }} WordPress version - runs-on: ubuntu-20.04 - strategy: - matrix: - wp: ['1', '2'] - steps: - - name: Create dirs. - run: | - mkdir -p package/woocommerce - mkdir -p tmp/woocommerce - - uses: actions/checkout@v3 + - 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: - path: package/woocommerce + 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.API_WP_LATEST_ARTIFACT }} + s3-bucket: ${{ secrets.REPORTS_BUCKET }} + + - name: Publish API Allure report + if: success() || ( failure() && steps.run-api-composite-action.conclusion == 'failure' ) + env: + GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }} + ENV_DESCRIPTION: wp-latest + run: | + gh workflow run publish-test-reports-release.yml \ + -f created_at="${{ needs.get-tag.outputs.created }}" \ + -f run_id=${{ github.run_id }} \ + -f run_number=${{ github.run_number }} \ + -f release_tag=${{ needs.get-tag.outputs.tag }} \ + -f artifact="${{ env.API_WP_LATEST_ARTIFACT }}" \ + -f env_description="${{ env.ENV_DESCRIPTION }}" \ + -f test_type="api" \ + --repo woocommerce/woocommerce-test-reports + + e2e-wp-latest: + name: E2E on WP Latest + runs-on: ubuntu-20.04 + needs: [get-tag, api-wp-latest] + permissions: + contents: read + env: + ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-report + ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/test-results/allure-results + steps: + - uses: actions/checkout@v3 - name: Setup WooCommerce Monorepo uses: ./.github/actions/setup-woocommerce-monorepo with: - build-filters: woocommerce + install-filters: woocommerce + build: false - - name: Fetch Asset ID - id: fetch_asset_id - uses: actions/github-script@v5 + - name: 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: - RELEASE_ID: ${{ github.event.inputs.release_id }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - REPO: ${{ github.repository }} + ADMIN_PASSWORD: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }} + ADMIN_USER: ${{ secrets.RELEASE_TEST_ADMIN_USER }} + ADMIN_USER_EMAIL: ${{ secrets.RELEASE_TEST_ADMIN_USER_EMAIL }} + BASE_URL: ${{ secrets.RELEASE_TEST_URL }} + CUSTOMER_PASSWORD: ${{ secrets.RELEASE_TEST_CUSTOMER_PASSWORD }} + CUSTOMER_USER: ${{ secrets.RELEASE_TEST_CUSTOMER_USER }} + DEFAULT_TIMEOUT_OVERRIDE: 120000 + E2E_MAX_FAILURES: 25 + RESET_SITE: true + + - name: Download 'e2e-update-wc' artifact + if: success() || failure() + uses: actions/download-artifact@v3 + with: + name: ${{ env.E2E_UPDATE_WC_ARTIFACT }} + path: plugins/woocommerce/tmp + + - name: Add allure-results from 'e2e-update-wc' + if: success() || failure() + working-directory: plugins/woocommerce + run: cp -r tmp/allure-results tests/e2e-pw/test-results + + - name: Generate E2E Test report. + if: success() || failure() + working-directory: plugins/woocommerce + run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }} + + - name: 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 + + - 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() + env: + GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }} + ENV_DESCRIPTION: wp-latest + run: | + gh workflow run publish-test-reports-release.yml \ + -f created_at="${{ needs.get-tag.outputs.created }}" \ + -f run_id=${{ github.run_id }} \ + -f run_number=${{ github.run_number }} \ + -f release_tag=${{ needs.get-tag.outputs.tag }} \ + -f artifact="${{ env.E2E_WP_LATEST_ARTIFACT }}" \ + -f env_description="${{ env.ENV_DESCRIPTION }}" \ + -f test_type="e2e" \ + --repo woocommerce/woocommerce-test-reports + + get-wp-versions: + name: Get WP L-1 & L-2 version numbers + needs: [get-tag] + runs-on: ubuntu-20.04 + permissions: + contents: read + outputs: + matrix: ${{ steps.get-versions.outputs.versions }} + tag: ${{ needs.get-tag.outputs.tag }} + created: ${{ needs.get-tag.outputs.created }} + steps: + - name: Create dirs + run: | + mkdir script + mkdir repo + + - name: Checkout + uses: actions/checkout@v3 + with: + path: repo + + - name: Copy script to get previous WP versions + run: cp repo/plugins/woocommerce/tests/e2e-pw/utils/wordpress.js script + + - name: Install axios + working-directory: script + run: npm install axios + + - name: Get version numbers + id: get-versions + uses: actions/github-script@v6 with: script: | - const script = require( './package/woocommerce/.github/workflows/scripts/fetch-asset-id.js' ) - await script({github, context, core}) + const { getPreviousTwoVersions } = require('./script/wordpress'); + const versions = await getPreviousTwoVersions(); + console.log(versions); + core.setOutput('versions', versions); - - name: Download WooCommerce release zip - working-directory: tmp + test-wp-versions: + name: Test against ${{ matrix.version.description }} (${{ matrix.version.number }}) + 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 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 test on wp-env with WordPress ${{ matrix.version.number }} (run ${{ github.run_number }}) + permissions: + contents: read + steps: + - name: Checkout WooCommerce repo + uses: actions/checkout@v3 + + - name: Setup WooCommerce Monorepo + uses: ./.github/actions/setup-woocommerce-monorepo + + - 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-wp-versions.outputs.tag }} --dir tmp + + - name: Replace `plugins/woocommerce` with unzipped woocommerce release build + run: unzip -d plugins -o tmp/woocommerce.zip + + - name: Downgrade WordPress version to ${{ matrix.version.number }} + working-directory: plugins/woocommerce run: | - curl https://api.github.com/repos/${{ github.repository }}/releases/assets/${{ steps.fetch_asset_id.outputs.asset_id }} -LJOH 'Accept: application/octet-stream' + pnpm exec wp-env run tests-cli "wp core update --version=${{ matrix.version.number }} --force" + pnpm exec wp-env run tests-cli "wp core update-db" - unzip woocommerce.zip -d woocommerce - rsync -a woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/ + - name: Verify environment details + working-directory: plugins/woocommerce + run: | + pnpm exec wp-env run tests-cli "wp core version" + pnpm exec wp-env run tests-cli "wp plugin list" + pnpm exec wp-env run tests-cli "wp theme list" + pnpm exec wp-env run tests-cli "wp user list" - - name: Load docker images and start containers. - working-directory: package/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: - LATEST_WP_VERSION_MINUS: ${{ matrix.wp }} - run: pnpm docker:up --filter=woocommerce + ALLURE_RESULTS_DIR: ${{ env.API_ALLURE_RESULTS_DIR }} + ALLURE_REPORT_DIR: ${{ env.API_ALLURE_REPORT_DIR }} - - name: Run tests command. - working-directory: package/woocommerce/plugins/woocommerce + - 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: - WC_E2E_SCREENSHOTS: 1 - E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }} - E2E_SLACK_CHANNEL: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }} - run: pnpm exec wc-e2e test:e2e + ALLURE_RESULTS_DIR: ${{ env.API_ALLURE_RESULTS_DIR }} + ALLURE_REPORT_DIR: ${{ env.API_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.API_WP_LATEST_X_ARTIFACT }} + s3-bucket: ${{ secrets.REPORTS_BUCKET }} + + - name: Publish API Allure report + if: success() || ( failure() && steps.run-api-composite-action.conclusion == 'failure' ) + env: + GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }} + ENV_DESCRIPTION: ${{ matrix.version.env_description }} + run: | + gh workflow run publish-test-reports-release.yml \ + -f created_at="${{ needs.get-wp-versions.outputs.created }}" \ + -f run_id=${{ github.run_id }} \ + -f run_number=${{ github.run_number }} \ + -f release_tag=${{ needs.get-wp-versions.outputs.tag }} \ + -f artifact="${{ env.API_WP_LATEST_X_ARTIFACT }}" \ + -f env_description="${{ env.ENV_DESCRIPTION }}" \ + -f test_type="api" \ + --repo woocommerce/woocommerce-test-reports + + - name: Run E2E tests + id: run-e2e-composite-action + timeout-minutes: 60 + uses: ./.github/actions/tests/run-e2e-tests + env: + E2E_MAX_FAILURES: 15 + ALLURE_RESULTS_DIR: ${{ env.E2E_ALLURE_RESULTS_DIR }} + ALLURE_REPORT_DIR: ${{ env.E2E_ALLURE_REPORT_DIR }} + DEFAULT_TIMEOUT_OVERRIDE: 120000 + with: + 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() && steps.run-e2e-composite-action.conclusion == 'failure' ) + env: + GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }} + ENV_DESCRIPTION: ${{ matrix.version.env_description }} + run: | + gh workflow run publish-test-reports-release.yml \ + -f created_at="${{ needs.get-wp-versions.outputs.created }}" \ + -f run_id=${{ github.run_id }} \ + -f run_number=${{ github.run_number }} \ + -f release_tag=${{ needs.get-wp-versions.outputs.tag }} \ + -f artifact="${{ env.E2E_WP_LATEST_X_ARTIFACT }}" \ + -f env_description="${{ env.ENV_DESCRIPTION }}" \ + -f test_type="e2e" \ + --repo woocommerce/woocommerce-test-reports + + test-php-versions: + name: Test against PHP ${{ matrix.php_version }} + runs-on: ubuntu-20.04 + needs: [get-tag] + strategy: + 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 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 test on wp-env with PHP ${{ matrix.php_version }} (run ${{ github.run_number }}) + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup WooCommerce Monorepo + uses: ./.github/actions/setup-woocommerce-monorepo + + - name: Launch WP Env + working-directory: plugins/woocommerce + env: + WP_ENV_PHP_VERSION: ${{ matrix.php_version }} + run: pnpm run env:test + + - name: Verify PHP version + working-directory: .github/workflows/scripts + env: + EXPECTED_PHP_VERSION: ${{ matrix.php_version }} + run: bash verify-php-version.sh + + - name: Download release zip + env: + GH_TOKEN: ${{ secrets.E2E_GH_TOKEN }} + run: gh release download ${{ needs.get-tag.outputs.tag }} --dir tmp + + - name: Replace `plugins/woocommerce` with unzipped woocommerce release build + run: unzip -d plugins -o tmp/woocommerce.zip + + - name: Run API tests + id: 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 }} + ALLURE_REPORT_DIR: ${{ env.API_ALLURE_REPORT_DIR }} + + - 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-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.API_ARTIFACT }} + s3-bucket: ${{ secrets.REPORTS_BUCKET }} + + - name: Publish API Allure report + if: success() || ( failure() && steps.run-api-composite-action.conclusion == 'failure' ) + env: + GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }} + ENV_DESCRIPTION: php-${{ matrix.php_version }} + run: | + gh workflow run publish-test-reports-release.yml \ + -f created_at="${{ needs.get-tag.outputs.created }}" \ + -f run_id=${{ github.run_id }} \ + -f run_number=${{ github.run_number }} \ + -f release_tag=${{ needs.get-tag.outputs.tag }} \ + -f artifact="${{ env.API_ARTIFACT }}" \ + -f env_description="${{ env.ENV_DESCRIPTION }}" \ + -f test_type="api" \ + --repo woocommerce/woocommerce-test-reports + + - name: Run E2E tests + id: run-e2e-composite-action + timeout-minutes: 60 + uses: ./.github/actions/tests/run-e2e-tests + env: + ALLURE_RESULTS_DIR: ${{ env.E2E_ALLURE_RESULTS_DIR }} + ALLURE_REPORT_DIR: ${{ env.E2E_ALLURE_REPORT_DIR }} + DEFAULT_TIMEOUT_OVERRIDE: 120000 + E2E_MAX_FAILURES: 15 + with: + 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() && steps.run-e2e-composite-action.conclusion == 'failure' ) + env: + GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }} + ENV_DESCRIPTION: php-${{ matrix.php_version }} + run: | + gh workflow run publish-test-reports-release.yml \ + -f created_at="${{ needs.get-tag.outputs.created }}" \ + -f run_id=${{ github.run_id }} \ + -f run_number=${{ github.run_number }} \ + -f release_tag=${{ needs.get-tag.outputs.tag }} \ + -f artifact="${{ env.E2E_ARTIFACT }}" \ + -f env_description="${{ env.ENV_DESCRIPTION }}" \ + -f test_type="e2e" \ + --repo woocommerce/woocommerce-test-reports test-plugins: - name: Smoke tests with ${{ matrix.plugin }} plugin installed + 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: Create dirs. - run: | - mkdir -p package/woocommerce - mkdir -p tmp/woocommerce - - - uses: actions/checkout@v3 - with: - path: package/woocommerce + - 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: - build-filters: woocommerce - - - name: Fetch Asset ID - id: fetch_asset_id - uses: actions/github-script@v5 + report-name: ${{ env.ARTIFACT_NAME }} + tests: upload-plugin.spec.js env: - RELEASE_ID: ${{ github.event.inputs.release_id }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - REPO: ${{ github.repository }} - with: - script: | - const script = require( './package/woocommerce/.github/workflows/scripts/fetch-asset-id.js' ) - await script({github, context, core}) - - - name: Download WooCommerce release zip - working-directory: tmp - run: | - curl https://api.github.com/repos/${{ github.repository }}/releases/assets/${{ steps.fetch_asset_id.outputs.asset_id }} -LJOH 'Accept: application/octet-stream' - - unzip woocommerce.zip -d woocommerce - rsync -a woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/ - - - name: Load docker images and start containers. - working-directory: package/woocommerce - env: - LATEST_WP_VERSION_MINUS: ${{ matrix.wp }} - run: pnpm docker:up --filter=woocommerce - - - name: Run tests command. - working-directory: package/woocommerce/plugins/woocommerce - env: - WC_E2E_SCREENSHOTS: 1 - E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }} - E2E_SLACK_CHANNEL: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }} - PLUGIN_REPOSITORY: ${{ matrix.private && secrets[matrix.repo] || matrix.repo }} - PLUGIN_NAME: ${{ matrix.plugin }} GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }} + 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: | - pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/upload-plugin.js - pnpm exec wc-e2e test:e2e + 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 diff --git a/.github/workflows/stalebot.yml b/.github/workflows/stalebot.yml index dc7e2c9b034..a512944ea96 100644 --- a/.github/workflows/stalebot.yml +++ b/.github/workflows/stalebot.yml @@ -3,11 +3,17 @@ on: schedule: - cron: '21 0 * * *' +permissions: {} + jobs: stale: if: | ! contains(github.event.issue.labels.*.name, 'type: enhancement') runs-on: ubuntu-20.04 + permissions: + contents: read + issues: write + pull-requests: write steps: - uses: actions/stale@v3 with: diff --git a/.github/workflows/syncpack.yml b/.github/workflows/syncpack.yml index f7898fa129f..3406ae02aee 100644 --- a/.github/workflows/syncpack.yml +++ b/.github/workflows/syncpack.yml @@ -1,32 +1,37 @@ name: Synchronize Dependencies with syncpack on: - # Run whenever a pull request is updated - pull_request: - branches: - - trunk - paths: - - '**/package.json' -jobs: - syncpack: - runs-on: ubuntu-latest - name: syncpack - steps: - - name: 'Checkout' - uses: actions/checkout@v3 - - - name: 'Setup node' - uses: actions/setup-node@v3 - with: - node-version: 16 + # Run whenever a pull request is updated + pull_request: + branches: + - trunk + paths: + - '**/package.json' - - name: 'Install Syncpack' - run: npm install -g syncpack@^8.2.4 - - - name: 'List Mismatches' - run: syncpack list-mismatches - - - name: 'Explain Remedy' - if: failure() - run: | - echo "Dependency version mismatch detected. This can usually be fixed automatically by updating the pinned version in \`.syncpackrc\` and then running: \`pnpm run sync-dependencies\`" - exit 1 +permissions: {} + +jobs: + syncpack: + runs-on: ubuntu-latest + permissions: + contents: read + name: syncpack + steps: + - name: 'Checkout' + uses: actions/checkout@v3 + + - name: 'Setup node' + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c + with: + node-version: 16 + + - name: 'Install Syncpack' + run: npm install -g syncpack@^9.8.4 + + - name: 'List Mismatches' + run: syncpack list-mismatches + + - name: 'Explain Remedy' + if: failure() + run: | + echo "Dependency version mismatch detected. This can usually be fixed automatically by updating the pinned version in \`.syncpackrc\` and then running: \`pnpm run sync-dependencies\`" + exit 1 diff --git a/.github/workflows/triage-label.yml b/.github/workflows/triage-label.yml index ef9c5852437..b7f6485a31d 100644 --- a/.github/workflows/triage-label.yml +++ b/.github/workflows/triage-label.yml @@ -4,9 +4,14 @@ on: issues: types: opened +permissions: {} + jobs: add_label: runs-on: ubuntu-20.04 + permissions: + contents: read + issues: write steps: - uses: actions/checkout@v3 - uses: actions-ecosystem/action-add-labels@v1 diff --git a/.github/workflows/triage-replies.yml b/.github/workflows/triage-replies.yml index a5429249306..d65d4842e0c 100644 --- a/.github/workflows/triage-replies.yml +++ b/.github/workflows/triage-replies.yml @@ -3,6 +3,9 @@ on: issues: types: - labeled + +permissions: {} + jobs: add-dev-comment: if: "github.event.label.name == 'needs: developer feedback'" diff --git a/.github/workflows/update-feedback-labels.yml b/.github/workflows/update-feedback-labels.yml index 34caa1c31a1..d619bd9fc00 100644 --- a/.github/workflows/update-feedback-labels.yml +++ b/.github/workflows/update-feedback-labels.yml @@ -1,6 +1,8 @@ name: 'Update contributor feedback labels on comment' on: 'issue_comment' +permissions: {} + jobs: feedback: if: | @@ -10,6 +12,8 @@ jobs: github.event.issue.state == 'open' && contains(github.event.issue.labels.*.name, 'needs: author feedback') runs-on: ubuntu-20.04 + permissions: + issues: write steps: - name: Add has feedback uses: actions-ecosystem/action-add-labels@v1 diff --git a/.gitignore b/.gitignore index 78d84df67a0..dfa505b1edd 100644 --- a/.gitignore +++ b/.gitignore @@ -72,8 +72,8 @@ yarn.lock # Editors nbproject/private/ -# Test Results -test-results.json +# E2E and API Test Results +test-results # Admin Feature config plugins/woocommerce/includes/react-admin/feature-config.php @@ -89,13 +89,6 @@ allure-results changes.json .env -# Playwright output & working files -/plugins/woocommerce/tests/e2e-pw/output -/plugins/woocommerce/tests/e2e-pw/report -/plugins/woocommerce/tests/e2e-pw/storage -/plugins/woocommerce/tests/e2e-pw/test-results.json -/plugins/woocommerce/tests/api-core-tests/output - # Turborepo .turbo diff --git a/.syncpackrc b/.syncpackrc index c5aa34c3f4b..832c6f62e33 100644 --- a/.syncpackrc +++ b/.syncpackrc @@ -1,6 +1,6 @@ { "dev": true, - "filter": "^(?:react|react-dom|typescript)$", + "filter": "^(?:config|react|react-dom|eslint|typescript|@typescript-eslint|@types/react|@wordpress|@types/wordpress__components|postcss|@types/node).*$", "indent": "\t", "overrides": true, "peer": true, @@ -13,11 +13,35 @@ "dependencies": [ "@typescript-eslint/**" ], - "pinVersion": "latest", + "dependencyTypes": [ + "devDependencies" + ], + "pinVersion": "^5.43.0", "packages": [ "**" ] }, + { + "dependencies": [ + "@types/react" + ], + "dependencyTypes": [ + "devDependencies" + ], + "pinVersion": "^17.0.2", + "packages": [ + "**" + ] + }, + { + "dependencies": [ + "config" + ], + "packages": [ + "**" + ], + "pinVersion": "3.3.7" + }, { "dependencies": [ "react", @@ -35,7 +59,110 @@ "packages": [ "**" ], - "pinVersion": "^4.8.3" + "pinVersion": "^4.9.5" + }, + { + "dependencies": [ + "eslint" + ], + "dependencyTypes": [ + "devDependencies" + ], + "packages": [ + "**" + ], + "pinVersion": "^8.32.0" + }, + { + "dependencies": [ + "@wordpress/eslint-plugin", + "@wordpress/babel-plugin-import-jsx-pragma", + "@wordpress/babel-preset-default", + "@wordpress/env", + "@wordpress/stylelint-config", + "@wordpress/prettier-config", + "@wordpress/scripts", + "@wordpress/jest-console", + "@wordpress/dependency-extraction-webpack-plugin", + "@wordpress/e2e-test-utils", + "@wordpress/jest-preset-default", + "@wordpress/postcss-plugins-preset", + "@wordpress/custom-templated-path-webpack-plugin", + "@wordpress/postcss-themes" + ], + "packages": [ + "**" + ], + "isIgnored": true + }, + { + "dependencies": [ + "@wordpress/block**", + "@wordpress/viewport" + ], + "packages": [ + "@woocommerce/product-editor", + "woocommerce/client/admin", + "@woocommerce/components" + ], + "isIgnored": true + }, + { + "dependencies": [ + "@wordpress/**" + ], + "packages": [ + "@woocommerce/experimental" + ], + "isIgnored": true + }, + { + "dependencies": [ + "@wordpress/**" + ], + "packages": [ + "**" + ], + "pinVersion": "wp-6.0" + }, + { + "dependencies": [ + "@types/wordpress__components" + ], + "packages": [ + "**" + ], + "pinVersion": "^19.10.3" + }, + { + "dependencies": [ + "postcss-loader" + ], + "dependencyTypes": [ + "devDependencies" + ], + "packages": [ + "**" + ], + "pinVersion": "^4.3.0" + }, + { + "dependencies": [ + "postcss" + ], + "packages": [ + "**" + ], + "pinVersion": "^8.4.7" + }, + { + "dependencies": [ + "@types/node" + ], + "packages": [ + "**" + ], + "pinVersion": "^16.18.18" } ] } diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000000..614fa2f46be --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +/.github/ @woocommerce/atlas diff --git a/README.md b/README.md index 8fef4f3d463..3c4ba63c3ac 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ To get up and running within the WooCommerce Monorepo, you will need to make sur ### Prerequisites -* [NVM](https://github.com/nvm-sh/nvm#installing-and-updating): While you can always install Node through other means, we recommend using NVM to ensure you're aligned with the version used by our development teams. Our repository contains [an `.nvmrc` file](.nvmrc) which helps ensure you are using the correct version of Node. -* [PNPM](https://pnpm.io/installation): Our repository utilizes PNPM to manage project dependencies and run various scripts involved in building and testing projects. -* [PHP 7.2+](https://www.php.net/manual/en/install.php): WooCommerce Core currently features a minimum PHP version of 7.2. It is also needed to run Composer and various project build scripts. -* [Composer](https://getcomposer.org/doc/00-intro.md): We use Composer to manage all of the dependencies for PHP packages and plugins. +- [NVM](https://github.com/nvm-sh/nvm#installing-and-updating): While you can always install Node through other means, we recommend using NVM to ensure you're aligned with the version used by our development teams. Our repository contains [an `.nvmrc` file](.nvmrc) which helps ensure you are using the correct version of Node. +- [PNPM](https://pnpm.io/installation): Our repository utilizes PNPM to manage project dependencies and run various scripts involved in building and testing projects. +- [PHP 7.2+](https://www.php.net/manual/en/install.php): WooCommerce Core currently features a minimum PHP version of 7.2. It is also needed to run Composer and various project build scripts. +- [Composer](https://getcomposer.org/doc/00-intro.md): We use Composer to manage all of the dependencies for PHP packages and plugins. Once you've installed all of the prerequisites, you can run the following commands to get everything working. @@ -32,24 +32,31 @@ Check out [our development guide](DEVELOPMENT.md) if you would like a more compr ## Repository Structure -* [**Plugins**](plugins): Our repository contains plugins that relate to or otherwise aid in the development of WooCommerce. - * [**WooCommerce Core**](plugins/woocommerce): The core WooCommerce plugin is available in the plugins directory. -* [**Packages**](packages): Contained within the packages directory are all of the [PHP](packages/php) and [JavaScript](packages/js) provided for the community. Some of these are internal dependencies and are marked with an `internal-` prefix. -* [**Tools**](tools): We also have a growing number of tools within our repository. Many of these are intended to be utilities and scripts for use in the monorepo, but, this directory may also contain external tools. +- [**Plugins**](plugins): Our repository contains plugins that relate to or otherwise aid in the development of WooCommerce. + - [**WooCommerce Core**](plugins/woocommerce): The core WooCommerce plugin is available in the plugins directory. +- [**Packages**](packages): Contained within the packages directory are all of the [PHP](packages/php) and [JavaScript](packages/js) provided for the community. Some of these are internal dependencies and are marked with an `internal-` prefix. +- [**Tools**](tools): We also have a growing number of tools within our repository. Many of these are intended to be utilities and scripts for use in the monorepo, but, this directory may also contain external tools. ## Reporting Security Issues + To disclose a security issue to our team, [please submit a report via HackerOne here](https://hackerone.com/automattic/). ## Support + This repository is not suitable for support. Please don't use our issue tracker for support requests, but for core WooCommerce issues only. Support can take place through the appropriate channels: -* If you have a problem, you may want to start with the [self help guide](https://docs.woocommerce.com/document/woocommerce-self-service-guide/). -* The [WooCommerce.com premium support portal](https://woocommerce.com/contact-us/) for customers who have purchased themes or extensions. -* [Our community forum on wp.org](https://wordpress.org/support/plugin/woocommerce) which is available for all WooCommerce users. -* [The Official WooCommerce Facebook Group](https://www.facebook.com/groups/advanced.woocommerce). -* For customizations, you may want to check our list of [WooExperts](https://woocommerce.com/experts/) or [Codeable](https://codeable.io/). +- If you have a problem, you may want to start with the [self help guide](https://docs.woocommerce.com/document/woocommerce-self-service-guide/). +- The [WooCommerce.com premium support portal](https://woocommerce.com/contact-us/) for customers who have purchased themes or extensions. +- [Our community forum on wp.org](https://wordpress.org/support/plugin/woocommerce) which is available for all WooCommerce users. +- [The Official WooCommerce Facebook Group](https://www.facebook.com/groups/advanced.woocommerce). +- For customizations, you may want to check our list of [WooExperts](https://woocommerce.com/experts/) or [Codeable](https://codeable.io/). -Support requests in issues on this repository will be closed on sight. +NOTE: Unfortunately, we are unable to honor support requests in issues on this repository; as a result, any requests submitted in this manner will be closed. + +## Community + +For peer to peer support, real-time announcements, and office hours, please [join our slack community](https://woocommerce.com/community-slack/)! ## Contributing to WooCommerce + If you have a patch or have stumbled upon an issue with WooCommerce core, you can contribute this back to the code. Please read our [contributor guidelines](https://github.com/woocommerce/woocommerce/blob/trunk/.github/CONTRIBUTING.md) for more information on how you can do this. diff --git a/bin/packages/js/extend-cart-checkout-block/.prettierrc.js.mustache b/bin/packages/js/extend-cart-checkout-block/.prettierrc.js.mustache new file mode 100644 index 00000000000..51b8aeb4150 --- /dev/null +++ b/bin/packages/js/extend-cart-checkout-block/.prettierrc.js.mustache @@ -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' ); diff --git a/bin/pre-push.sh b/bin/pre-push.sh index 024e1f21b3a..bccdc2587e7 100755 --- a/bin/pre-push.sh +++ b/bin/pre-push.sh @@ -27,12 +27,13 @@ pnpm exec syncpack -- list-mismatches if [ $? -ne 0 ]; then echo "You must sync the dependencies listed above before you can push this branch." - echo "This can usually be accomplished automatically by updating the pinned version in `.syncpackrc` and then running \`pnpm run sync-dependencies\`." + echo "This can usually be accomplished automatically by updating the pinned version in \`.syncpackrc\` and then running \`pnpm run sync-dependencies\`." exit 1 fi -# Ensure both branches are tracked or check-changelogger-use will fail. -git checkout $PROTECTED_BRANCH --quiet -git checkout $CURRENT_BRANCH --quiet +# Ensure both branches are tracked or check-changelogger-use will fail. Note we pass hooksPath +# to avoid running the pre-commit hook. +git -c core.hooksPath=/dev/null checkout $PROTECTED_BRANCH --quiet +git -c core.hooksPath=/dev/null checkout $CURRENT_BRANCH --quiet php tools/monorepo/check-changelogger-use.php $PROTECTED_BRANCH $CURRENT_BRANCH diff --git a/changelog.txt b/changelog.txt index e30f9f9135c..6005a873509 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,846 @@ == Changelog == += 7.6.1 2023-04-26 = + +**WooCommerce** + +* Fix - Fix regression in supporting nested date query arguments in HPOS. [#37827](https://github.com/woocommerce/woocommerce/pull/37827) +* Fix - Sync up date_column_name default for orders table, between stats and table data. [#37927](https://github.com/woocommerce/woocommerce/pull/37927) +* Fix - Revert "Change Variations form shown in Variations tab when there are no variations created (#36957)" [#37889](https://github.com/woocommerce/woocommerce/pull/37889) +* Fix – Revert changes to use window.fetch in legacy cart JS [#37463](https://github.com/woocommerce/woocommerce/pull/37463) +* Update - Update WooCommerce Blocks to 9.8.5 [#37921](https://github.com/woocommerce/woocommerce/pull/37921) + += 7.6.0 2023-04-13 = + +**WooCommerce** + +* Fix - Fix incorrect usage of dispatch, useSelect, and setState calls in homescreen along with settings and onboarding package [#37641](https://github.com/woocommerce/woocommerce/pull/37641) +* Fix - Do not attempt to cache order during order creation (HPOS). [#37569](https://github.com/woocommerce/woocommerce/pull/37569) +* Fix - Add default value when calling get_option for woocommerce_task_list_tracked_completed_tasks. [#37397](https://github.com/woocommerce/woocommerce/pull/37397) +* Fix - When order meta data is saved via HPOS, it should be backfilled to the CPT data store. [#36593](https://github.com/woocommerce/woocommerce/pull/36593) +* Fix - Overwrite clone method to prevent duplicate data when saving a clone. [#37313](https://github.com/woocommerce/woocommerce/pull/37313) +* Fix - Add default button padding to TT2 stylesheet to fix some visual issues in WP 5.9 and 6.0 [#37018](https://github.com/woocommerce/woocommerce/pull/37018) +* Fix - Added skydropx slug back to shipping partners list so that it can be installed through the shipping task [#37286](https://github.com/woocommerce/woocommerce/pull/37286) +* Fix - Add HPOS compat for admin report functions. [#36650](https://github.com/woocommerce/woocommerce/pull/36650) +* Fix - Add HPOS compat for wc-user-functions.php. [#36650](https://github.com/woocommerce/woocommerce/pull/36650) +* Fix - Add support for null inputs to pnpm wc_add_number_precision [#36891](https://github.com/woocommerce/woocommerce/pull/36891) +* Fix - Add support for `after`, `before`, `modified_after` and `modified_before` params in local timezone. [#36650](https://github.com/woocommerce/woocommerce/pull/36650) +* Fix - Add validation when saving attributes and variations [#37046](https://github.com/woocommerce/woocommerce/pull/37046) +* Fix - Also delete when order type is placehoder, since it was created by HPOS. [#36650](https://github.com/woocommerce/woocommerce/pull/36650) +* Fix - Corrects a class reference in the ProductDownloadsServiceProvider. [#37052](https://github.com/woocommerce/woocommerce/pull/37052) +* Fix - Corrects a variable name in Reports\Stock\Stats. It was missed during the last name change. [#37057](https://github.com/woocommerce/woocommerce/pull/37057) +* Fix - Corrects class namespaces in Onboarding. It was missed during last restructuring. [#37056](https://github.com/woocommerce/woocommerce/pull/37056) +* Fix - Corrects imported classes. Class names should not begin with a backslash. [#37058](https://github.com/woocommerce/woocommerce/pull/37058) +* Fix - Ensure product importer imports all lines in a CSV file. [#36839](https://github.com/woocommerce/woocommerce/pull/36839) +* Fix - Fetch order first to refresh cache before returning prop. [#36650](https://github.com/woocommerce/woocommerce/pull/36650) +* Fix - Fix 0 rendered on short-circuit evaluation. [#37104](https://github.com/woocommerce/woocommerce/pull/37104) +* Fix - Fix ArrayUtil::get_value_or_default method not behaving as documented for null array values [#37053](https://github.com/woocommerce/woocommerce/pull/37053) +* Fix - Fix blank screen is displayed during OBW when using WP5.9 [#36903](https://github.com/woocommerce/woocommerce/pull/36903) +* Fix - Fix duplicated global attribute [#37109](https://github.com/woocommerce/woocommerce/pull/37109) +* Fix - fixed bug where jetpack connection owner field was assumed to be username when its actually display name [#37170](https://github.com/woocommerce/woocommerce/pull/37170) +* Fix - Fixed payments recommendations pane in WooCommerce Payment Settings using the wrong image prop [#37259](https://github.com/woocommerce/woocommerce/pull/37259) +* Fix - Fixes filtering by attributes in the Analytics Orders and Variations reports. [#37223](https://github.com/woocommerce/woocommerce/pull/37223) +* Fix - Fix incorrect VAT exempt behaviour on shop page when prices are exclusive of tax. [#33991](https://github.com/woocommerce/woocommerce/pull/33991) +* Fix - Fix React rendering falsy value in marketing page. [#37227](https://github.com/woocommerce/woocommerce/pull/37227) +* Fix - Fix the inability to apply a coupon whose code is "0" [#36924](https://github.com/woocommerce/woocommerce/pull/36924) +* Fix - fix typo in variable name [#36759](https://github.com/woocommerce/woocommerce/pull/36759) +* Fix - Fix unit test snapshots due to a dependency version change [#36435](https://github.com/woocommerce/woocommerce/pull/36435) +* Fix - Fix variations exported as draft being imported as draft (and thus remaining invisible) [#36933](https://github.com/woocommerce/woocommerce/pull/36933) +* Fix - Fix WP data resolution (`invalidateResolution`) not working with WP 5.9 in marketing page. [#37198](https://github.com/woocommerce/woocommerce/pull/37198) +* Fix - Handle date arguments in OrderTableQuery correctly by adjusting their timezones before running. [#36650](https://github.com/woocommerce/woocommerce/pull/36650) +* Fix - Load same stylesheets in the Site Editor as in the frontend [#36911](https://github.com/woocommerce/woocommerce/pull/36911) +* Fix - Loco Translate and wp-cli compatibility for woocommerce-admin translation files [#36739](https://github.com/woocommerce/woocommerce/pull/36739) +* Fix - Override react version to 17.0.2 [#37087](https://github.com/woocommerce/woocommerce/pull/37087) +* Fix - Prevent possible warning arising from use of woocommerce_wp_* family of functions. [#37026](https://github.com/woocommerce/woocommerce/pull/37026) +* Fix - Record values for toggled checkboxes/features in settings [#37242](https://github.com/woocommerce/woocommerce/pull/37242) +* Fix - Restore the sort order when orders are cached. [#36650](https://github.com/woocommerce/woocommerce/pull/36650) +* Fix - Treat order as seperate resource when validating for webhook since it's not necessarily a CPT anymore. [#36650](https://github.com/woocommerce/woocommerce/pull/36650) +* Fix - Update Customers report with latest user data after editing user. [#37237](https://github.com/woocommerce/woocommerce/pull/37237) +* Add - Add "Create a new campaign" modal in Campaigns card in Multichannel Marketing page. [#37044](https://github.com/woocommerce/woocommerce/pull/37044) +* Add - Add a cache for orders, to use when custom order tables are enabled [#35014](https://github.com/woocommerce/woocommerce/pull/35014) +* Add - Add an encoding selector to the product importer [#36819](https://github.com/woocommerce/woocommerce/pull/36819) +* Add - Add Campaigns card into Multichannel Marketing page. [#36735](https://github.com/woocommerce/woocommerce/pull/36735) +* Add - Added images support for the payment recommendations transaction processors [#37230](https://github.com/woocommerce/woocommerce/pull/37230) +* Add - Added `woocommerce_widget_layered_nav_filters_start/end` hooks around layered nav filters widget [#36705](https://github.com/woocommerce/woocommerce/pull/36705) +* Add - Add introduction banner to multichannel marketing page. [#37110](https://github.com/woocommerce/woocommerce/pull/37110) +* Add - Add marketplace suggestions and multichannel marketing information to WC Tracker. [#37017](https://github.com/woocommerce/woocommerce/pull/37017) +* Add - Add new feature flag for the product edit blocks experience [#37137](https://github.com/woocommerce/woocommerce/pull/37137) +* Add - Add productBlockEditorSettings script to be used for the Product Block Editor. [#37123](https://github.com/woocommerce/woocommerce/pull/37123) +* Add - Add support for new countries in WCPay [#36906](https://github.com/woocommerce/woocommerce/pull/36906) +* Add - Add wp-json/wc-admin/shipping-partner-suggestions API endpoint [#37155](https://github.com/woocommerce/woocommerce/pull/37155) +* Add - Allow sorting by menu_order in products widget. [#37002](https://github.com/woocommerce/woocommerce/pull/37002) +* Add - Create editor skeleton on add/edit product pages [#37023](https://github.com/woocommerce/woocommerce/pull/37023) +* Add - Creating product entity in auto-draft status, and adding support for retrieving preexisting products. [#37064](https://github.com/woocommerce/woocommerce/pull/37064) +* Add - Fixed image array in edit context for product/variations endpoint. [#28498](https://github.com/woocommerce/woocommerce/pull/28498) +* Add - Initial e2e tests for new product editor. [#36902](https://github.com/woocommerce/woocommerce/pull/36902) +* Add - Log to order notes when coupons are removed or applied. [#30642](https://github.com/woocommerce/woocommerce/pull/30642) +* Add - Make WC_Order::get_tax_location accessible publicly through a wrapper function. [#36953](https://github.com/woocommerce/woocommerce/pull/36953) +* Add - Update product post rest config when block editor feature is enabled. [#37206](https://github.com/woocommerce/woocommerce/pull/37206) +* Update - Update WooCommerce Blocks to 9.8.4 [#37492](https://github.com/woocommerce/woocommerce/pull/37492) +* Update - Update WooCommerce Blocks to 9.8.3 [#37477](https://github.com/woocommerce/woocommerce/pull/37477) +* Update - Update WooCommerce Blocks to 9.8.2 [#37373](https://github.com/woocommerce/woocommerce/pull/37373) +* Update - Add tabs and sections placeholders in product blocks template [#37174](https://github.com/woocommerce/woocommerce/pull/37174) +* Update - Change the default date used on Revenue and Orders report to 'date_paid' and create spotlight on both reports [#36653](https://github.com/woocommerce/woocommerce/pull/36653) +* Update - Change Variations form shown in Variations tab when there are no variations created [#36957](https://github.com/woocommerce/woocommerce/pull/36957) +* Update - Moving currencyContext to relevant package, and updating all references. [#36959](https://github.com/woocommerce/woocommerce/pull/36959) +* Update - Moving some components out of core and into product-editor package. [#36945](https://github.com/woocommerce/woocommerce/pull/36945) +* Update - Moving use-product-helper and related product hooks to product editor package. [#37006](https://github.com/woocommerce/woocommerce/pull/37006) +* Update - Refresh data source poller transients on wc_admin_daily [#37027](https://github.com/woocommerce/woocommerce/pull/37027) +* Update - Remove accordion from "Other payment providers" in payment task [#37205](https://github.com/woocommerce/woocommerce/pull/37205) +* Update - Remove Cart2Cart option from add product task [#37285](https://github.com/woocommerce/woocommerce/pull/37285) +* Update - Show link to store settings when stock management is disabled. [#37140](https://github.com/woocommerce/woocommerce/pull/37140) +* Update - Update create-wc-extension script within woocommerce-admin. [#36917](https://github.com/woocommerce/woocommerce/pull/36917) +* Update - Update imports of product slot fills to new @woocommerce/product-editor library [#36830](https://github.com/woocommerce/woocommerce/pull/36830) +* Update - Update obw payment gateways [#37233](https://github.com/woocommerce/woocommerce/pull/37233) +* Update - Update playwright api-core-tests to associate orders with real products to prevent extension issues for those that validate product ids [#37243](https://github.com/woocommerce/woocommerce/pull/37243) +* Update - Update playwright api-core-tests to handle cases where extensions add to shipping methods [#37239](https://github.com/woocommerce/woocommerce/pull/37239) +* Update - Update product template by adding the list price and sale price blocks. [#37211](https://github.com/woocommerce/woocommerce/pull/37211) +* Update - Updates automated release testing workflow to use Playwright [#36598](https://github.com/woocommerce/woocommerce/pull/36598) +* Update - Update template of product type to include product name block. [#37132](https://github.com/woocommerce/woocommerce/pull/37132) +* Update - Update the date modified field for an order when a refund for it is successfully processed. [#37047](https://github.com/woocommerce/woocommerce/pull/37047) +* Update - Update WooCommerce BLocks to 9.8.0 [#37210](https://github.com/woocommerce/woocommerce/pull/37210) +* Update - Update WooCommerce Blocks to 9.8.1 [#37238](https://github.com/woocommerce/woocommerce/pull/37238) +* Update - Updating rest namespace for product posttype to version 3. [#37028](https://github.com/woocommerce/woocommerce/pull/37028) +* Update - Use the currently activated theme color for completed tasks strikethough [#37001](https://github.com/woocommerce/woocommerce/pull/37001) +* Dev - Add @woocommerce/admin-layout package. [#37094](https://github.com/woocommerce/woocommerce/pull/37094) +* Dev - Add CES data store to @woocommerce/customer-effort-score [#37252](https://github.com/woocommerce/woocommerce/pull/37252) +* Dev - Add existing global attribute layout #36944 [#36944](https://github.com/woocommerce/woocommerce/pull/36944) +* Dev - Add missing woocommerce_run_on_woocommerce_admin_updated hook for the scheduled action registered in RemoteInboxNotificationsEngine [#36768](https://github.com/woocommerce/woocommerce/pull/36768) +* Dev - add wpLogin import to wc-baseline-load.js [#36940](https://github.com/woocommerce/woocommerce/pull/36940) +* Dev - Convert "Allow backorders?" into radio buttons [#37282](https://github.com/woocommerce/woocommerce/pull/37282) +* Dev - Fix lint issues [#36988](https://github.com/woocommerce/woocommerce/pull/36988) +* Dev - Fix the value of `UPDATE_WC` environment variable in the daily k6 performance tests. [#37049](https://github.com/woocommerce/woocommerce/pull/37049) +* Dev - Move CES components and utilities to @woocommerce/customer-effort-score [#37112](https://github.com/woocommerce/woocommerce/pull/37112) +* Dev - Move hook to confirm unsaved form changes to navigation package [#36752](https://github.com/woocommerce/woocommerce/pull/36752) +* Dev - Move product utils into product editor package [#36730](https://github.com/woocommerce/woocommerce/pull/36730) +* Dev - Set up React Fast Refresh in woocommerce-admin [#37165](https://github.com/woocommerce/woocommerce/pull/37165) +* Dev - Show "Stock status" as a collection of radio buttons [#37278](https://github.com/woocommerce/woocommerce/pull/37278) +* Dev - Show a message for variable products [#37185](https://github.com/woocommerce/woocommerce/pull/37185) +* Dev - Support E2E testing of draft releases. [#36997](https://github.com/woocommerce/woocommerce/pull/36997) +* Dev - Sync @wordpress package versions via syncpack. [#37034](https://github.com/woocommerce/woocommerce/pull/37034) +* Tweak - Add productId dependency when getting the product by id in ProductPage [#37152](https://github.com/woocommerce/woocommerce/pull/37152) +* Tweak - Add tracking for local pickup method in Checkout [#36847](https://github.com/woocommerce/woocommerce/pull/36847) +* Tweak - Add Tracks events for product inventory tab interactions. [#37202](https://github.com/woocommerce/woocommerce/pull/37202) +* Tweak - Change Avalara CTA copy in tax task to Download [#37224](https://github.com/woocommerce/woocommerce/pull/37224) +* Tweak - Make sure 'safe_text' settings are rendered as 'text' inputs for compatibility. [#37154](https://github.com/woocommerce/woocommerce/pull/37154) +* Tweak - Prevent 'woocommerce_ajax_order_items_removed' from generating PHP warnings. [#37178](https://github.com/woocommerce/woocommerce/pull/37178) +* Tweak - Rename "Manage stock?" label to "Stock management". [#37135](https://github.com/woocommerce/woocommerce/pull/37135) +* Tweak - Trigger event `woocommerce_attributes_saved` following successful product meta box ajax update. [#36943](https://github.com/woocommerce/woocommerce/pull/36943) +* Tweak - Visual tweaks for shipping partner banners [#37229](https://github.com/woocommerce/woocommerce/pull/37229) +* Performance - Bypass Action Scheduler for customer updates. [#37265](https://github.com/woocommerce/woocommerce/pull/37265) +* Performance - Switch wc_product_attributes_lookup table management to use truncate and dbDelta over drop table [#36872](https://github.com/woocommerce/woocommerce/pull/36872) +* Enhancement - Add 'display_context' argument to wc_get_price_to_display(). [#25080](https://github.com/woocommerce/woocommerce/pull/25080) +* Enhancement - Added woocommerce_reduce_order_item_stock action hook [#34721](https://github.com/woocommerce/woocommerce/pull/34721) +* Enhancement - Add the support for the C&C Blocks in declaring compatibility feature [#36426](https://github.com/woocommerce/woocommerce/pull/36426) + + += 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 & 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** + +* Update - Update WooCommerce Blocks to 9.4.4. [#36982](https://github.com/woocommerce/woocommerce/pull/36982) + + += 7.4.0 2023-02-14 = + +**WooCommerce** + +* Fix - Add support for sorting by includes param. [#36215](https://github.com/woocommerce/woocommerce/pull/36215) +* Fix - Allow product tab navigation without prompting for unsaved changes [#36235](https://github.com/woocommerce/woocommerce/pull/36235) +* Fix - Convert HTML to blocks in product variation description [#36241](https://github.com/woocommerce/woocommerce/pull/36241) +* Fix - Decode HTML entities in CategoryBreadcrumbs. [#36321](https://github.com/woocommerce/woocommerce/pull/36321) +* Fix - Decode HTML entities in CategoryFieldItem. [#36367](https://github.com/woocommerce/woocommerce/pull/36367) +* Fix - Ensure order emails are responsive in most email clients, including when the current language is RTL. [#36310](https://github.com/woocommerce/woocommerce/pull/36310) +* Fix - Ensures product variation sort order is correctly persisted. [#36343](https://github.com/woocommerce/woocommerce/pull/36343) +* Fix - Ensure wc_get_order() works without arguments when HPOS is enabled. [#36496](https://github.com/woocommerce/woocommerce/pull/36496) +* Fix - Fix "Save changes?" modal saves the options after selecting the 'Discard' option [#36160](https://github.com/woocommerce/woocommerce/pull/36160) +* Fix - Fix attributes/options lists corrupt render #36236 [#36236](https://github.com/woocommerce/woocommerce/pull/36236) +* Fix - Fix bug when filtering for customer_id=0. [#36216](https://github.com/woocommerce/woocommerce/pull/36216) +* Fix - Fix deprecated usage of ${var} in strings [#36439](https://github.com/woocommerce/woocommerce/pull/36439) +* Fix - Fix edit attribute modal terms list [#36186](https://github.com/woocommerce/woocommerce/pull/36186) +* Fix - Fixes editing of child product reviews. [#35888](https://github.com/woocommerce/woocommerce/pull/35888) +* Fix - Fix for product filters when 'shop' page is the front page. [#36224](https://github.com/woocommerce/woocommerce/pull/36224) +* Fix - Fix issue where attribute term dropdown was not adhering to sort order setting. [#36047](https://github.com/woocommerce/woocommerce/pull/36047) +* Fix - Fix navigation between variations and tab selection [#36239](https://github.com/woocommerce/woocommerce/pull/36239) +* Fix - Fix notices styling in Twenty Twenty-Three [#36475](https://github.com/woocommerce/woocommerce/pull/36475) +* Fix - Fix overlapping header elements on product page [#36495](https://github.com/woocommerce/woocommerce/pull/36495) +* Fix - Fix product table dropdown issue on mobile. [#36046](https://github.com/woocommerce/woocommerce/pull/36046) +* Fix - Fix reordering list items error [#36296](https://github.com/woocommerce/woocommerce/pull/36296) +* Fix - Fix REST API order refunds enpoint when HPOS is active, and make v2 orders endpoint compatible with HPOS [#36308](https://github.com/woocommerce/woocommerce/pull/36308) +* Fix - Fix settings tables styles [#36531](https://github.com/woocommerce/woocommerce/pull/36531) +* Fix - Fix tax task showing as not completed after setting up tax [#36468](https://github.com/woocommerce/woocommerce/pull/36468) +* Fix - Fix the signature mismatch affecting wc cli commands ability to fetch user subscription data. [#36240](https://github.com/woocommerce/woocommerce/pull/36240) +* Fix - Fix total count query of orders within Analytics reports data store. [#35971](https://github.com/woocommerce/woocommerce/pull/35971) +* Fix - Hide Variations section when it is empty [#36202](https://github.com/woocommerce/woocommerce/pull/36202) +* Fix - Improve accessibility of the coupon code label, in the context of the cart page. [#36247](https://github.com/woocommerce/woocommerce/pull/36247) +* Fix - Improve the way we retrieve the alt text property for product attachments. [#35009](https://github.com/woocommerce/woocommerce/pull/35009) +* Fix - Load wc_empty_cart function for REST API calls. [#36182](https://github.com/woocommerce/woocommerce/pull/36182) +* Fix - Make HPOS UX more consistent with posts UI (so that same e2e tests passes for both). [#36282](https://github.com/woocommerce/woocommerce/pull/36282) +* Fix - Make order edit messages compatible with both posts and theorder object. [#36485](https://github.com/woocommerce/woocommerce/pull/36485) +* Fix - Make sure the tracking shortcode only operates in orders with billing information. [#33735](https://github.com/woocommerce/woocommerce/pull/33735) +* Fix - Remove persisted query on return to parent product from variation [#36365](https://github.com/woocommerce/woocommerce/pull/36365) +* Fix - Reset variation form if a new variation is given [#36078](https://github.com/woocommerce/woocommerce/pull/36078) +* Fix - Restore the pre-7.2.0 behavior for single product quantity inputs. [#36460](https://github.com/woocommerce/woocommerce/pull/36460) +* Fix - Set child orders to be children of current order parent before deleting for consistency. [#36218](https://github.com/woocommerce/woocommerce/pull/36218) +* Fix - Skip custom search for HPOS API queries as it's handled already. [#36213](https://github.com/woocommerce/woocommerce/pull/36213) +* Fix - Use Imagick functions to set parallel thread count instead of direct putenv call as suggested in https://core.trac.wordpress.org/ticket/36534#comment:129. [#35339](https://github.com/woocommerce/woocommerce/pull/35339) +* Fix - When adjusting download permissions, confirm the child products have not been removed. [#36431](https://github.com/woocommerce/woocommerce/pull/36431) +* Add - Add ability to filter variations by local attributes in REST API [#36201](https://github.com/woocommerce/woocommerce/pull/36201) +* Add - Add an admin notice about the upcoming PHP version requirement change for PHP 7.2 users [#36444](https://github.com/woocommerce/woocommerce/pull/36444) +* Add - Added a slot for extending the app with a homescreen header banner [#36467](https://github.com/woocommerce/woocommerce/pull/36467) +* Add - Added a slot for ProgressHeader and ProgressTitle component [#36482](https://github.com/woocommerce/woocommerce/pull/36482) +* Add - Add edit button to variations list items [#36079](https://github.com/woocommerce/woocommerce/pull/36079) +* Add - Added slot for tasklist completion slotfill [#36487](https://github.com/woocommerce/woocommerce/pull/36487) +* Add - Add endpoint to create all product variations [#35980](https://github.com/woocommerce/woocommerce/pull/35980) +* Add - Add exit prompt CES for users editing orders when tracking is enabled. [#35762](https://github.com/woocommerce/woocommerce/pull/35762) +* Add - Adding delayed spotlight to feedback button on current product page. [#35865](https://github.com/woocommerce/woocommerce/pull/35865) +* Add - Adding feedback button to activity bar on classic product page. [#35810](https://github.com/woocommerce/woocommerce/pull/35810) +* Add - Adding JS data store for ProductForm. [#36430](https://github.com/woocommerce/woocommerce/pull/36430) +* Add - Adding the WooProductSectionItem slot within the product editor general tab. [#36331](https://github.com/woocommerce/woocommerce/pull/36331) +* Add - Add initial product form PHP helper class to add new fields. [#36093](https://github.com/woocommerce/woocommerce/pull/36093) +* Add - Additional error logging within the CSV Exporter framework. [#34802](https://github.com/woocommerce/woocommerce/pull/34802) +* Add - Add multichannel marketing API [#36453](https://github.com/woocommerce/woocommerce/pull/36453) +* Add - Add new filter to add additional clauses for SQL statement in Variations report [#36378](https://github.com/woocommerce/woocommerce/pull/36378) +* Add - Add new product form API for extending the new Product Form MVP. [#36165](https://github.com/woocommerce/woocommerce/pull/36165) +* Add - Add Options section to new product experience form. [#35910](https://github.com/woocommerce/woocommerce/pull/35910) +* Add - Add product tour to new product management experience [#36428](https://github.com/woocommerce/woocommerce/pull/36428) +* Add - Add product variation form [#36033](https://github.com/woocommerce/woocommerce/pull/36033) +* Add - Add product variation General section [#36081](https://github.com/woocommerce/woocommerce/pull/36081) +* Add - Add product variation header actions and persistence [#36155](https://github.com/woocommerce/woocommerce/pull/36155) +* Add - Add product variation image [#36133](https://github.com/woocommerce/woocommerce/pull/36133) +* Add - Add product variation navigation component [#36076](https://github.com/woocommerce/woocommerce/pull/36076) +* Add - Add product variations flag to only show work in development [#36311](https://github.com/woocommerce/woocommerce/pull/36311) +* Add - Add product variation title to page header [#36085](https://github.com/woocommerce/woocommerce/pull/36085) +* Add - Add Product variation visibility toggle [#36020](https://github.com/woocommerce/woocommerce/pull/36020) +* Add - Add single product variation sections [#36051](https://github.com/woocommerce/woocommerce/pull/36051) +* Add - Adds support for a 'required' argument when invoking `wc_dropdown_variation_attribute_options()`. [#34579](https://github.com/woocommerce/woocommerce/pull/34579) +* Add - Add support for sorting by order metadata in HPOS queries. [#36403](https://github.com/woocommerce/woocommerce/pull/36403) +* Add - Add WooOnboardingTaskListHeader, woocommerce_admin_experimental_onboarding_tasklists filter, and woocommerce_onboarding_task_list_header Slot to task list [#36519](https://github.com/woocommerce/woocommerce/pull/36519) +* Add - Include tax options in pricing section [#36299](https://github.com/woocommerce/woocommerce/pull/36299) +* Add - Persist active tab on refresh [#36112](https://github.com/woocommerce/woocommerce/pull/36112) +* Add - Persist variations order on product save [#36109](https://github.com/woocommerce/woocommerce/pull/36109) +* Add - Product variation quantity status indicator [#35982](https://github.com/woocommerce/woocommerce/pull/35982) +* Add - Product variations card should have a fixed height. [#36053](https://github.com/woocommerce/woocommerce/pull/36053) +* Add - Remove manage_stock 'parent' value before saving the variation [#36234](https://github.com/woocommerce/woocommerce/pull/36234) +* Add - Run ces exit prompt when product import abandoned. [#35996](https://github.com/woocommerce/woocommerce/pull/35996) +* Add - Scroll newly added product attribute into view in new product management experience [#36447](https://github.com/woocommerce/woocommerce/pull/36447) +* Add - Show product CES footer on product tour close [#36516](https://github.com/woocommerce/woocommerce/pull/36516) +* Add - Truncate attribute option name to a max of 32 chars in variations list [#36134](https://github.com/woocommerce/woocommerce/pull/36134) +* Add - Trying experimental slot context with product editor fills. [#36333](https://github.com/woocommerce/woocommerce/pull/36333) +* Add - Using slotfill to insert attributes section in the product editor. [#36483](https://github.com/woocommerce/woocommerce/pull/36483) +* Add - Using slotfill to insert images section in product editor. [#36461](https://github.com/woocommerce/woocommerce/pull/36461) +* Update - Update woocommerce-blocks to 9.4.3. [#36736](https://github.com/woocommerce/woocommerce/pull/36736) +* Update - Adding WooProductFieldItem slot to product details section. [#36315](https://github.com/woocommerce/woocommerce/pull/36315) +* Update - Add permalink_template and generated_slug to products REST API response. [#36497](https://github.com/woocommerce/woocommerce/pull/36497) +* Update - Auto generate variations on option changes [#36188](https://github.com/woocommerce/woocommerce/pull/36188) +* Update - Bundled version of Action Scheduler updated to 3.5.4. [#36433](https://github.com/woocommerce/woocommerce/pull/36433) +* Update - Customers REST API endpoint will now return user metadata only when requester has an administrator role [#36408](https://github.com/woocommerce/woocommerce/pull/36408) +* Update - Disable irrelevant product tabs when variations exist [#35939](https://github.com/woocommerce/woocommerce/pull/35939) +* Update - Migrate shipping section in product editor to slot fill. [#36534](https://github.com/woocommerce/woocommerce/pull/36534) +* Update - Move product management feature flag down to experimental. [#36552](https://github.com/woocommerce/woocommerce/pull/36552) +* Update - Reimplementing product details fields in product editor as slot fills. [#36368](https://github.com/woocommerce/woocommerce/pull/36368) +* Update - Update api-core-tests readme to include a guide for writing tests [#35978](https://github.com/woocommerce/woocommerce/pull/35978) +* Update - Update store-details test snapshot to reflect updated select-control [#35808](https://github.com/woocommerce/woocommerce/pull/35808) +* Update - Update WooCommerce Blocks to 9.4.0 [#36524](https://github.com/woocommerce/woocommerce/pull/36524) +* Update - Update WooCommerce Blocks to 9.4.1 [#36553](https://github.com/woocommerce/woocommerce/pull/36553) +* Update - Update WooCommerce Blocks to 9.4.2 [#36624](https://github.com/woocommerce/woocommerce/pull/36624) +* Dev - Add advanced setting option [#36380](https://github.com/woocommerce/woocommerce/pull/36380) +* Dev - Add experimental SlotFill for task list footer [#36527](https://github.com/woocommerce/woocommerce/pull/36527) +* Dev - Cleanup product task experiment [#35950](https://github.com/woocommerce/woocommerce/pull/35950) +* Dev - Consistent folder structure for E2E and API test results [#35907](https://github.com/woocommerce/woocommerce/pull/35907) +* Dev - Fix docblock type annotations for $meta_value. [#33853](https://github.com/woocommerce/woocommerce/pull/33853) +* Dev - Fix flakiness of the `can save industry changes when navigating back to "Store Details"` E2E test. [#36260](https://github.com/woocommerce/woocommerce/pull/36260) +* Dev - Make shopper tests passable on daily smoke test site. [#35873](https://github.com/woocommerce/woocommerce/pull/35873) +* Dev - Move product attribute fetching logic into a separate hook [#36354](https://github.com/woocommerce/woocommerce/pull/36354) +* Dev - Update TaskLists::add_task() to reflect changes in TaskList::add_task() [#36104](https://github.com/woocommerce/woocommerce/pull/36104) +* Dev - Update the browserslist config for legacy client JS to match Wordpress. [#36264](https://github.com/woocommerce/woocommerce/pull/36264) +* Dev - Upgrade PHPUnit to v8 [#36273](https://github.com/woocommerce/woocommerce/pull/36273) +* Tweak - Corrects a typo in the i18n/states.php file, relating to our list of Iranian states. [#36457](https://github.com/woocommerce/woocommerce/pull/36457) +* Tweak - Derive product type from product attributes [#36243](https://github.com/woocommerce/woocommerce/pull/36243) +* Tweak - Fix typo in a function comment. [#36122](https://github.com/woocommerce/woocommerce/pull/36122) +* Tweak - Fix units in function doc comment [#36353](https://github.com/woocommerce/woocommerce/pull/36353) +* Tweak - Make related products check more robust against wrong transients. [#34742](https://github.com/woocommerce/woocommerce/pull/34742) +* Tweak - Makes it possible to use an `add_meta_boxes_` style hook in the HPOS editor, for parity with the traditional post editor. [#35999](https://github.com/woocommerce/woocommerce/pull/35999) +* Tweak - Minor adjustments to the ProductForm API [#36414](https://github.com/woocommerce/woocommerce/pull/36414) +* Tweak - Redirect to new product experience when in experiment group [#36381](https://github.com/woocommerce/woocommerce/pull/36381) +* Tweak - Refactor AttributeField into sub-components. [#35997](https://github.com/woocommerce/woocommerce/pull/35997) +* Tweak - Update product links when new product management experience is enabled [#36382](https://github.com/woocommerce/woocommerce/pull/36382) +* Tweak - Updates and improves the docblocks for methods WC_Order::get_total() and WC_Order::get_subtotal(). [#34385](https://github.com/woocommerce/woocommerce/pull/34385) +* Tweak - Validation of Norweigan postcodes has been added. [#36277](https://github.com/woocommerce/woocommerce/pull/36277) +* Performance - Speed up HPOS search query by using group by instead of distinct. [#35897](https://github.com/woocommerce/woocommerce/pull/35897) +* Enhancement - Add context to countries shipping to prefix [#36254](https://github.com/woocommerce/woocommerce/pull/36254) +* Enhancement - Adds new order status filters for bacs and cheque email instructions. [#35849](https://github.com/woocommerce/woocommerce/pull/35849) +* Enhancement - Improves handling of the single product page quantity selector, in relation to variable products. [#36087](https://github.com/woocommerce/woocommerce/pull/36087) +* Enhancement - Remove default WooCommerce button styles if using a block theme which adds button styles in theme.json [#36225](https://github.com/woocommerce/woocommerce/pull/36225) + + += 7.3.0 2023-01-10 = + +**WooCommerce** + +* Fix - Remove redundant Pinterest plugin from marketing task [#36158](https://github.com/woocommerce/woocommerce/pull/36158) +* Fix - Corrects a hard-coded reference to the WP post meta table within the HPOS Migration Helper, that would fail on some sites. [#36100](https://github.com/woocommerce/woocommerce/pull/36100) +* Fix - Add a blank space between the emoji and the message within a notice popup [#35698](https://github.com/woocommerce/woocommerce/pull/35698) +* Fix - Add a data migration for changed New Zealand and Ukraine state codes [#35960](https://github.com/woocommerce/woocommerce/pull/35960) +* Fix - Add back missing scss files from assets. [#35624](https://github.com/woocommerce/woocommerce/pull/35624) +* Fix - Address HPOS synchronization issues relating to the deletion of orders. [#35723](https://github.com/woocommerce/woocommerce/pull/35723) +* Fix - Avoid a potential fatal error when forming edit-order URLs. [#35995](https://github.com/woocommerce/woocommerce/pull/35995) +* Fix - Fix call of array_key_exists in SSR. [#35598](https://github.com/woocommerce/woocommerce/pull/35598) +* Fix - Fix ellipsis dropdown menu is mostly hidden within the task list [#35949](https://github.com/woocommerce/woocommerce/pull/35949) +* Fix - Fixes fatal error resulting from translating the WooCommerce main menu. [#35695](https://github.com/woocommerce/woocommerce/pull/35695) +* Fix - Fix get orders REST API endpoint when using 'search' or 'parent' and HPOS is enabled [#35818](https://github.com/woocommerce/woocommerce/pull/35818) +* Fix - Fix handling of non-ASCII product attributes when the attributes lookup table is in use [#34432](https://github.com/woocommerce/woocommerce/pull/34432) +* Fix - Fix handling of statuses in orders list table (HPOS). [#35370](https://github.com/woocommerce/woocommerce/pull/35370) +* Fix - Fix logo icon for Google Listings and Ads. [#35732](https://github.com/woocommerce/woocommerce/pull/35732) +* Fix - Fix product tab to be shown on production build [#35976](https://github.com/woocommerce/woocommerce/pull/35976) +* Fix - Fix regexp used for filtering country dropdown on the store details step #35941 [#35942](https://github.com/woocommerce/woocommerce/pull/35942) +* Fix - Fix the gap in the featured product checkbox [#35710](https://github.com/woocommerce/woocommerce/pull/35710) +* Fix - Fix tooltips not appearing in the orders list admin page. [#35638](https://github.com/woocommerce/woocommerce/pull/35638) +* Fix - Fix unread note count on inbox panel [#35396](https://github.com/woocommerce/woocommerce/pull/35396) +* Fix - Fix unsaved modal propmt to not be shown during form submission [#35657](https://github.com/woocommerce/woocommerce/pull/35657) +* Fix - Fix version in template and function docblocks. [#35473](https://github.com/woocommerce/woocommerce/pull/35473) +* Fix - Fix WooCommerce Admin client React build warnings and remove unnecessary scss imports. [#35930](https://github.com/woocommerce/woocommerce/pull/35930) +* Fix - Fix wrong query param in onboarding product api call [#35926](https://github.com/woocommerce/woocommerce/pull/35926) +* Fix - If order types have not been registered, do not attempt to count orders. [#35820](https://github.com/woocommerce/woocommerce/pull/35820) +* Fix - Make the 'unprotected upload directory' notice dismissable. [#33544](https://github.com/woocommerce/woocommerce/pull/33544) +* Fix - Update Playwright to 1.28.0 and explicitly set PHP version in GH action [#35679](https://github.com/woocommerce/woocommerce/pull/35679) +* Fix - When importing product CSV, ensure line breaks within header columns do not break the import process. [#35880](https://github.com/woocommerce/woocommerce/pull/35880) +* Add - Add CES exit prompt for setting pages, when tracking is enabled. [#35761](https://github.com/woocommerce/woocommerce/pull/35761) +* Add - Add CES feedback functionality to the share feedback button within the Product MVP. [#35690](https://github.com/woocommerce/woocommerce/pull/35690) +* Add - Add Denmark postcode validation. [#35653](https://github.com/woocommerce/woocommerce/pull/35653) +* Add - Add exit prompt logic to get feedback if users leave product pages without saving when tracking is enabled. [#35728](https://github.com/woocommerce/woocommerce/pull/35728) +* Add - Add FormFileUpload component [#35358](https://github.com/woocommerce/woocommerce/pull/35358) +* Add - Add HPOS information to WC Tracker. [#35446](https://github.com/woocommerce/woocommerce/pull/35446) +* Add - Add new option to create new attribute within add attribute modal. [#35100](https://github.com/woocommerce/woocommerce/pull/35100) +* Add - Add new product management breadcrumbs to header [#35596](https://github.com/woocommerce/woocommerce/pull/35596) +* Add - Add new Product MVP CES footer for gathering feedback on the new product management screen. [#35652](https://github.com/woocommerce/woocommerce/pull/35652) +* Add - Add one-click installation to recommended extensions in Marketing page. [#35542](https://github.com/woocommerce/woocommerce/pull/35542) +* Add - Add pagination to variations list [#35979](https://github.com/woocommerce/woocommerce/pull/35979) +* Add - Add product settings menu in header [#35592](https://github.com/woocommerce/woocommerce/pull/35592) +* Add - Add product tab headers and move sections to respective tabs [#35862](https://github.com/woocommerce/woocommerce/pull/35862) +* Add - Add product variations list to new product management experience [#35889](https://github.com/woocommerce/woocommerce/pull/35889) +* Add - Add support for custom order types in HPOS admin UI. [#35658](https://github.com/woocommerce/woocommerce/pull/35658) +* Add - Add the woocommerce_order_applied_coupon hook [#35616](https://github.com/woocommerce/woocommerce/pull/35616) +* Add - Add tracks events for 'product_view_product_click' and 'product_view_product_dismiss' [#35582](https://github.com/woocommerce/woocommerce/pull/35582) +* Add - Introduces action `woocommerce_order_list_table_restrict_manage_orders` as an equivalent of the legacy `restrict_manage_posts` hook. [#36000](https://github.com/woocommerce/woocommerce/pull/36000) +* Add - Open categories menu list when the user focus the category field [#35606](https://github.com/woocommerce/woocommerce/pull/35606) +* Update - Match country name or ' - region' when filtering country select control #36120 [#36159](https://github.com/woocommerce/woocommerce/pull/36159) +* Update - Update WooCommerce Blocks to 9.1.3 [#36125](https://github.com/woocommerce/woocommerce/pull/36125) +* Update - Adapt the summary text in the product management form. [#35717](https://github.com/woocommerce/woocommerce/pull/35717) +* Update - Add Codisto for WooCommerce to free extensions list [#36009](https://github.com/woocommerce/woocommerce/pull/36009) +* Update - Add experimental open menu on focus option to the attribute and attribute term input fields. [#35758](https://github.com/woocommerce/woocommerce/pull/35758) +* Update - Add missing tracks events [#35262](https://github.com/woocommerce/woocommerce/pull/35262) +* Update - Add Pinterest for WooCommerce to free extensions list [#36003](https://github.com/woocommerce/woocommerce/pull/36003) +* Update - Automatically create the custom order tables if the corresponding feature is enabled [#35357](https://github.com/woocommerce/woocommerce/pull/35357) +* Update - Disable TikTok in the OBW [#35924](https://github.com/woocommerce/woocommerce/pull/35924) +* Update - Include taxes migration in MigrationHelper::migrate_country_states [#35967](https://github.com/woocommerce/woocommerce/pull/35967) +* Update - Increase consistency in relation to the way taxonomy term ordering is persisted. [#34645](https://github.com/woocommerce/woocommerce/pull/34645) +* Update - Make product form header and actions responsive for smaller viewports [#35623](https://github.com/woocommerce/woocommerce/pull/35623) +* Update - Remove welcome to woocommerce store note [#35342](https://github.com/woocommerce/woocommerce/pull/35342) +* Update - Surface Amazon Pay as "Additional Payment Options" for UK/EU/JP [#35726](https://github.com/woocommerce/woocommerce/pull/35726) +* Update - Update api-core-tests readme to reference correct directory for .env file [#35759](https://github.com/woocommerce/woocommerce/pull/35759) +* Update - Update country data in api-core-tests to prevent numerous test data updates [#35557](https://github.com/woocommerce/woocommerce/pull/35557) +* Update - update FAQ in readme consumed by .org [#35696](https://github.com/woocommerce/woocommerce/pull/35696) +* Update - Update WooCommerce Blocks to 9.1.1 [#36004](https://github.com/woocommerce/woocommerce/pull/36004) +* Update - Update wording for In-App Marketplace tour. [#35929](https://github.com/woocommerce/woocommerce/pull/35929) +* Update - Updating all CES events to support two questions in modal. [#35680](https://github.com/woocommerce/woocommerce/pull/35680) +* Dev - Allow the user to select multiple images in the Media Library [#35722](https://github.com/woocommerce/woocommerce/pull/35722) +* Dev - Check if blocks have been added to rich text editors before updating value [#35626](https://github.com/woocommerce/woocommerce/pull/35626) +* Dev - Make e2e tests compatible with nightly and release smoke test sites. [#35492](https://github.com/woocommerce/woocommerce/pull/35492) +* Dev - Move file picker by clicking card into the MediaUploader component [#35738](https://github.com/woocommerce/woocommerce/pull/35738) +* Dev - Update the "can manually add a variation" E2E test to prevent automatic creation of variations from all attributes. [#36008](https://github.com/woocommerce/woocommerce/pull/36008) +* Tweak - Avoid deprecation notices under PHP 8.1 when calling wp_parse_url(). [#35648](https://github.com/woocommerce/woocommerce/pull/35648) +* Tweak - Correct the usage of 'address' and 'addresses' within `wc_get_account_menu_items()`. [#32026](https://github.com/woocommerce/woocommerce/pull/32026) +* Tweak - Create ProductForm component to merge similar structures between AddProductPage and EditProductPage [#35783](https://github.com/woocommerce/woocommerce/pull/35783) +* Tweak - Improves efficiency of code responsible for determining plugin IDs (during feature compatibility checks). [#35727](https://github.com/woocommerce/woocommerce/pull/35727) +* Tweak - Make the formatted shipping address available via the `woocommerce_cart_no_shipping_available_html` hook. [#30723](https://github.com/woocommerce/woocommerce/pull/30723) +* Tweak - Make the OrdersTableDataStore init_order_record() and get_order_data_for_ids() functions protected rather than private [#35829](https://github.com/woocommerce/woocommerce/pull/35829) +* Tweak - Move CSS about notice outside of .woocommerce class scope [#35912](https://github.com/woocommerce/woocommerce/pull/35912) +* Tweak - Resolve an error in the product tracking code by testing to see if the `post_type` query var is set before checking its value. [#34501](https://github.com/woocommerce/woocommerce/pull/34501) +* Tweak - Simplify wording within the customer emails for on-hold orders. [#31886](https://github.com/woocommerce/woocommerce/pull/31886) +* Tweak - WooCommerce has now been tested up to WordPress 6.1.x. [#35985](https://github.com/woocommerce/woocommerce/pull/35985) +* Performance - Split CALC_FOUND_ROW query into seperate count query for better performance. [#35468](https://github.com/woocommerce/woocommerce/pull/35468) +* Enhancement - Add a bottom padding to the whole form [#35721](https://github.com/woocommerce/woocommerce/pull/35721) +* Enhancement - Add a confirmation modal when the user tries to navigate away with unsaved changes [#35625](https://github.com/woocommerce/woocommerce/pull/35625) +* Enhancement - Add a default placeholder title for newly added attributes and always show remove button for attributes [#35904](https://github.com/woocommerce/woocommerce/pull/35904) +* Enhancement - Add help tip for Product Image and Product Gallery meta boxes [#35834](https://github.com/woocommerce/woocommerce/pull/35834) +* Enhancement - Add support for product attribute taxonomy template [#35617](https://github.com/woocommerce/woocommerce/pull/35617) +* Enhancement - Add warning banner that informs the user if they have conflicting tax display settings [#36010](https://github.com/woocommerce/woocommerce/pull/36010) +* Enhancement - Change the width of pricing fields, SKU and Shipping Class to 50% in big screens (>960px) in new product management experience [#35545](https://github.com/woocommerce/woocommerce/pull/35545) +* Enhancement - Fix price field currency symbol position [#35718](https://github.com/woocommerce/woocommerce/pull/35718) +* Enhancement - Improve element stacking in modals on tablet and mobile [#35733](https://github.com/woocommerce/woocommerce/pull/35733) +* Security - Customers REST API endpoint will now return user metadata only when requester has an administrator role [#36408](https://github.com/woocommerce/woocommerce/pull/36408). + += 7.2.3 2023-1-9 + +**WooCommerce Blocks 8.9.4** + +* Fix - fatal error in WordPress 5.8 when creating a post or page. #7496 +* Fix - hangs in the block editor with WordPress 5.8. #8095 +* Fix - Filter by Attribute block crashing in the editor of WordPress 5.8. #8101 + += 7.2.2 2022-12-21 = + +** WooCommerce** + +* Fix - Corrects a hard-coded reference to the WP post meta table within the HPOS Migration Helper, that would fail on some sites. [#36100](https://github.com/woocommerce/woocommerce/pull/36100) + += 7.2.1 2022-12-16 = + +**WooCommerce** + +* Update - Include taxes migration in MigrationHelper::migrate_country_states [#35967](https://github.com/woocommerce/woocommerce/pull/35967) + += 7.2.0 2022-12-14 = + +**WooCommerce** + +* Fix - Corrects a hard-coded reference to the WP post meta table within the HPOS Migration Helper, that would fail on some sites. [#36100](https://github.com/woocommerce/woocommerce/pull/36100) +* Fix - Drop usage of WP 5.9 function in the product quantity selector template. [#36054](https://github.com/woocommerce/woocommerce/pull/36054) +* Fix - Add a data migration for changed New Zealand and Ukraine state codes [#35669](https://github.com/woocommerce/woocommerce/pull/35669) +* Fix - Fix error in onboarding wizard when plugin is activated but includes unexpected output. [#35866](https://github.com/woocommerce/woocommerce/pull/35866) +* Fix - Increased margin so that overflow modal content doesn't clip header [#35780](https://github.com/woocommerce/woocommerce/pull/35780) +* Fix - Added default additional content to emails via filter woocommerce_email_additional_content_. [#35195](https://github.com/woocommerce/woocommerce/pull/35195) +* Fix - Corrects the currency symbol for Libyan Dinar (LYD). [#35395](https://github.com/woocommerce/woocommerce/pull/35395) +* Fix - Fix 'Invalid payment method' error upon double click on Delete button of Payment methods table [#30884](https://github.com/woocommerce/woocommerce/pull/30884) +* Fix - Fix bg color that was not covering the full page [#35476](https://github.com/woocommerce/woocommerce/pull/35476) +* Fix - Fix class name for class FirstDownlaodableProduct [#35383](https://github.com/woocommerce/woocommerce/pull/35383) +* Fix - Fixed "Unsupported operand types" error. [#34327](https://github.com/woocommerce/woocommerce/pull/34327) +* Fix - Fix inconsistent return type of class WC_Shipping_Rate->get_shipping_tax() [#35453](https://github.com/woocommerce/woocommerce/pull/35453) +* Fix - Fix invalid wcadmin_install_plugin_error event props [#35411](https://github.com/woocommerce/woocommerce/pull/35411) +* Fix - Fix JS error when the business step is accessed directly via URL without completing the previous steps [#35045](https://github.com/woocommerce/woocommerce/pull/35045) +* Fix - fix popper position for in-app marketplace tour [#35278](https://github.com/woocommerce/woocommerce/pull/35278) +* Fix - Fix WooCommerce icons not loading in the site editor. [#35532](https://github.com/woocommerce/woocommerce/pull/35532) +* Fix - FQCN for WP_Error in PHPDoc. [#35305](https://github.com/woocommerce/woocommerce/pull/35305) +* Fix - Make the user search metabox for orders show the same information for the loaded user and for search results [#35244](https://github.com/woocommerce/woocommerce/pull/35244) +* Fix - Override filter_meta_data method, since it should be a no-op anyway. [#35192](https://github.com/woocommerce/woocommerce/pull/35192) +* Fix - Remove the direct dependency on `$_POST` when validating checkout data. [#35329](https://github.com/woocommerce/woocommerce/pull/35329) +* Fix - Revert change that auto collapses the product short description field. [#35213](https://github.com/woocommerce/woocommerce/pull/35213) +* Fix - Skip flaky settings API test [#35338](https://github.com/woocommerce/woocommerce/pull/35338) +* Fix - Update Playwright from 1.26.1 to 1.27.1 [#35106](https://github.com/woocommerce/woocommerce/pull/35106) +* Fix - When the minimum and maximum quantity are identical, render the quantity input and set it to disabled. [#34282](https://github.com/woocommerce/woocommerce/pull/34282) +* Add - Add "Empty Trash" functionality to HPOS list table. [#35489](https://github.com/woocommerce/woocommerce/pull/35489) +* Add - Add add attribute modal to the attribute field in the new product management MVP [#34999](https://github.com/woocommerce/woocommerce/pull/34999) +* Add - Add add new option for the category dropdown within the product MVP [#35132](https://github.com/woocommerce/woocommerce/pull/35132) +* Add - Add contextual product more menu [#35447](https://github.com/woocommerce/woocommerce/pull/35447) +* Add - Added a guided tour for WooCommerce Extensions page [#35278](https://github.com/woocommerce/woocommerce/pull/35278) +* Add - Added npm script for Playwright API Core Tests [#35283](https://github.com/woocommerce/woocommerce/pull/35283) +* Add - Added states for Senegal. [#35199](https://github.com/woocommerce/woocommerce/pull/35199) +* Add - Added the "Tour the WooCommerce Marketplace" task to onboarding tasks list [#35278](https://github.com/woocommerce/woocommerce/pull/35278) +* Add - Added Ukrainian subdivisions. [#35493](https://github.com/woocommerce/woocommerce/pull/35493) +* Add - Adding attribute edit modal for new product screen. [#35269](https://github.com/woocommerce/woocommerce/pull/35269) +* Add - Add manual stock management section to product management experience [#35047](https://github.com/woocommerce/woocommerce/pull/35047) +* Add - Add new Category dropdown field to the new Product Management screen. [#34400](https://github.com/woocommerce/woocommerce/pull/34400) +* Add - add new track events for in-app marketplace tour [#35278](https://github.com/woocommerce/woocommerce/pull/35278) +* Add - Add option and modal to create new attribute terms within MVP attribute modal. [#35131](https://github.com/woocommerce/woocommerce/pull/35131) +* Add - Add placeholder to description field [#35286](https://github.com/woocommerce/woocommerce/pull/35286) +* Add - Add playwright api-core-tests for data crud operations [#35347](https://github.com/woocommerce/woocommerce/pull/35347) +* Add - Add playwright api-core-tests for payment gateways crud operations [#35279](https://github.com/woocommerce/woocommerce/pull/35279) +* Add - Add playwright api-core-tests for product reviews crud operations [#35163](https://github.com/woocommerce/woocommerce/pull/35163) +* Add - Add playwright api-core-tests for product variations crud operations [#35355](https://github.com/woocommerce/woocommerce/pull/35355) +* Add - Add playwright api-core-tests for reports crud operations [#35388](https://github.com/woocommerce/woocommerce/pull/35388) +* Add - Add playwright api-core-tests for settingss crud operations [#35253](https://github.com/woocommerce/woocommerce/pull/35253) +* Add - Add playwright api-core-tests for system status crud operations [#35254](https://github.com/woocommerce/woocommerce/pull/35254) +* Add - Add playwright api-core-tests for webhooks crud operations [#35292](https://github.com/woocommerce/woocommerce/pull/35292) +* Add - Add Product description title in old editor for clarification. [#35154](https://github.com/woocommerce/woocommerce/pull/35154) +* Add - Add product inventory advanced section [#35164](https://github.com/woocommerce/woocommerce/pull/35164) +* Add - Add product management description to new product management experience [#34961](https://github.com/woocommerce/woocommerce/pull/34961) +* Add - Add product state badge to product form header [#35460](https://github.com/woocommerce/woocommerce/pull/35460) +* Add - Add product title to header when available [#35431](https://github.com/woocommerce/woocommerce/pull/35431) +* Add - Add scheduled sale support to new product edit page. [#34538](https://github.com/woocommerce/woocommerce/pull/34538) +* Add - Adds new Inbox Note to provide more information about WooCommerce Payments to users who dismiss the WCPay promo but say that they want more information in the exit survey. [#35581](https://github.com/woocommerce/woocommerce/pull/35581) +* Add - Add summary to new product page experience [#35201](https://github.com/woocommerce/woocommerce/pull/35201) +* Add - Include order datastore information in status report. [#35487](https://github.com/woocommerce/woocommerce/pull/35487) +* Add - Make it possible to add custom bulk action handling to the admin order list screen (when HPOS is enabled). [#35442](https://github.com/woocommerce/woocommerce/pull/35442) +* Add - Set In-App Marketplace Tour as completed on tour close [#35278](https://github.com/woocommerce/woocommerce/pull/35278) +* Add - When custom order tables are not authoritative, admin UI requests will be redirected to the matching legacy order screen as appropriate. [#35463](https://github.com/woocommerce/woocommerce/pull/35463) +* Update - Woo Blocks 8.9.2 [#35805](https://github.com/woocommerce/woocommerce/pull/35805) +* Update - Comment: Update WooCommerce Blocks to 8.7.2 [#35101](https://github.com/woocommerce/woocommerce/pull/35101) +* Update - Comment: Update WooCommerce Blocks to 8.7.3 [#35219](https://github.com/woocommerce/woocommerce/pull/35219) +* Update - Comment: Update WooCommerce Blocks to 8.9.1 [#35564](https://github.com/woocommerce/woocommerce/pull/35564) +* Update - CustomOrdersTableController::custom_orders_table_usage_is_enabled returns now false if the HPOS feature is disabled [#35597](https://github.com/woocommerce/woocommerce/pull/35597) +* Update - Disable inventory stock toggle when product stock management is disabled [#35059](https://github.com/woocommerce/woocommerce/pull/35059) +* Update - Improve the loading time of WooCommerce setup widget for large databases [#35334](https://github.com/woocommerce/woocommerce/pull/35334) +* Update - Permit showing a guided tour for WooCommerce Extensions page on desktops only [#35278](https://github.com/woocommerce/woocommerce/pull/35278) +* Update - Remove adding and managing products note [#35319](https://github.com/woocommerce/woocommerce/pull/35319) +* Update - Remove first downloadable product note [#35318](https://github.com/woocommerce/woocommerce/pull/35318) +* Update - Remove InsightFirstProductAndPayment note [#35309](https://github.com/woocommerce/woocommerce/pull/35309) +* Update - Remove insight on first sale note [#35341](https://github.com/woocommerce/woocommerce/pull/35341) +* Update - Remove manage store activity note [#35320](https://github.com/woocommerce/woocommerce/pull/35320) +* Update - Remove Popover.Slot usage and make use of exported SelectControlMenuSlot. [#35353](https://github.com/woocommerce/woocommerce/pull/35353) +* Update - Remove update store details note [#35322](https://github.com/woocommerce/woocommerce/pull/35322) +* Update - Update Array checks in playwright api-core-tests as some of the existing tests would produce false positives [#35462](https://github.com/woocommerce/woocommerce/pull/35462) +* Update - Update playwright api-core-tests for shipping crud operations [#35332](https://github.com/woocommerce/woocommerce/pull/35332) +* Update - Update playwright api-core-tests to execute for both base test environment and base JN environment with WooCommerce [#35522](https://github.com/woocommerce/woocommerce/pull/35522) +* Update - Update products task list UI [#35611](https://github.com/woocommerce/woocommerce/pull/35611) +* Update - Update ShippingLabelBanner add_meta_box action to only trigger on shop_order pages and remove deprecated function call. [#35212](https://github.com/woocommerce/woocommerce/pull/35212) +* Update - Update WooCommerce Blocks to 8.9.0 [#35521](https://github.com/woocommerce/woocommerce/pull/35521) +* Dev - Add variation price shortcut [#34948](https://github.com/woocommerce/woocommerce/pull/34948) +* Dev - Cleanup and deprecate unused Task properties and methods [#35450](https://github.com/woocommerce/woocommerce/pull/35450) +* Dev - Enable Playwright tests on Daily Smoke Test workflow and upload its Allure reports to S3 bucket. [#35114](https://github.com/woocommerce/woocommerce/pull/35114) +* Dev - Move product action buttons to header menu [#35214](https://github.com/woocommerce/woocommerce/pull/35214) +* Dev - Revert the changes introduced in PR #35282 [#35337](https://github.com/woocommerce/woocommerce/pull/35337) +* Dev - Show a dismissible snackbar if the server responds with an error [#35160](https://github.com/woocommerce/woocommerce/pull/35160) +* Dev - Update api-core-tests readme for consistency with new command and updates to other commands too. [#35303](https://github.com/woocommerce/woocommerce/pull/35303) +* Dev - Updated the COT plugin URL now that this feature can be enabled in a different way. [#34990](https://github.com/woocommerce/woocommerce/pull/34990) +* Dev - Update the list of tags for WC plugin on .org [#35573](https://github.com/woocommerce/woocommerce/pull/35573) +* Dev - Update unit test install script for db sockets. [#35152](https://github.com/woocommerce/woocommerce/pull/35152) +* Dev - Use plugins/woocommerce/tests/e2e-pw folder for saving test outputs [#35206](https://github.com/woocommerce/woocommerce/pull/35206) +* Dev - Uses the globa-setup.js to setup permalinks structure [#35282](https://github.com/woocommerce/woocommerce/pull/35282) +* Tweak - Move HPOS hook woocommerce_before_delete_order before deleting order. [#35517](https://github.com/woocommerce/woocommerce/pull/35517) +* Tweak - Adds new filter `woocommerce_get_customer_payment_tokens_limit` to set limit on number of payment methods fetched within the My Account page. [#29850](https://github.com/woocommerce/woocommerce/pull/29850) +* Tweak - Add source parameter for calls to the subscriptions endpoint on WooCommerce.com [#35051](https://github.com/woocommerce/woocommerce/pull/35051) +* Tweak - Fix @version header in form-login.php [#35479](https://github.com/woocommerce/woocommerce/pull/35479) +* Tweak - Move HPOS hook woocommerce_before_delete_order before deleting order. [#35517](https://github.com/woocommerce/woocommerce/pull/35517) +* Tweak - typo fix [#35111](https://github.com/woocommerce/woocommerce/pull/35111) +* Tweak - Unwrap product page input props and pass via getInputProps [#35034](https://github.com/woocommerce/woocommerce/pull/35034) +* Tweak - Updates the currency symbol used for the Azerbaijani manat. [#30605](https://github.com/woocommerce/woocommerce/pull/30605) +* Tweak - Use new Tooltip component instead of EnrichedLabel [#35024](https://github.com/woocommerce/woocommerce/pull/35024) +* Enhancement - Change the product info section title to Product Details [#35255](https://github.com/woocommerce/woocommerce/pull/35255) +* Enhancement - Fix the display of letter descenders in the shipping class dropdown menu [#35258](https://github.com/woocommerce/woocommerce/pull/35258) +* Enhancement - Improve the communication around required and optional [#35266](https://github.com/woocommerce/woocommerce/pull/35266) +* Enhancement - Increase the spacing between the shipping box illustration and the dimensions fields [#35259](https://github.com/woocommerce/woocommerce/pull/35259) +* Enhancement - Optimize query usage in the Onboarding tasks [#35065](https://github.com/woocommerce/woocommerce/pull/35065) +* Enhancement - Remove some placeholder values [#35267](https://github.com/woocommerce/woocommerce/pull/35267) +* Enhancement - Replace the trash can icon in the attribute list [#35133](https://github.com/woocommerce/woocommerce/pull/35133) +* Enhancement - Select the current new added shipping class [#35123](https://github.com/woocommerce/woocommerce/pull/35123) +* Enhancement - Tweaks the PR template for GitHub pull requests [#34597](https://github.com/woocommerce/woocommerce/pull/34597) + + += 7.2.1 2022-12-16 = + +**WooCommerce** + +* Update - Include taxes migration in MigrationHelper::migrate_country_states [#35967](https://github.com/woocommerce/woocommerce/pull/35967) + += 7.2.0 2022-12-14 = + +**WooCommerce** + +* Fix - Drop usage of WP 5.9 function in the product quantity selector template. [#36054](https://github.com/woocommerce/woocommerce/pull/36054) +* Fix - Add a data migration for changed New Zealand and Ukraine state codes [#35669](https://github.com/woocommerce/woocommerce/pull/35669) +* Fix - Fix error in onboarding wizard when plugin is activated but includes unexpected output. [#35866](https://github.com/woocommerce/woocommerce/pull/35866) +* Fix - Increased margin so that overflow modal content doesn't clip header [#35780](https://github.com/woocommerce/woocommerce/pull/35780) +* Fix - Added default additional content to emails via filter woocommerce_email_additional_content_. [#35195](https://github.com/woocommerce/woocommerce/pull/35195) +* Fix - Corrects the currency symbol for Libyan Dinar (LYD). [#35395](https://github.com/woocommerce/woocommerce/pull/35395) +* Fix - Fix 'Invalid payment method' error upon double click on Delete button of Payment methods table [#30884](https://github.com/woocommerce/woocommerce/pull/30884) +* Fix - Fix bg color that was not covering the full page [#35476](https://github.com/woocommerce/woocommerce/pull/35476) +* Fix - Fix class name for class FirstDownlaodableProduct [#35383](https://github.com/woocommerce/woocommerce/pull/35383) +* Fix - Fixed "Unsupported operand types" error. [#34327](https://github.com/woocommerce/woocommerce/pull/34327) +* Fix - Fix inconsistent return type of class WC_Shipping_Rate->get_shipping_tax() [#35453](https://github.com/woocommerce/woocommerce/pull/35453) +* Fix - Fix invalid wcadmin_install_plugin_error event props [#35411](https://github.com/woocommerce/woocommerce/pull/35411) +* Fix - Fix JS error when the business step is accessed directly via URL without completing the previous steps [#35045](https://github.com/woocommerce/woocommerce/pull/35045) +* Fix - fix popper position for in-app marketplace tour [#35278](https://github.com/woocommerce/woocommerce/pull/35278) +* Fix - Fix WooCommerce icons not loading in the site editor. [#35532](https://github.com/woocommerce/woocommerce/pull/35532) +* Fix - FQCN for WP_Error in PHPDoc. [#35305](https://github.com/woocommerce/woocommerce/pull/35305) +* Fix - Make the user search metabox for orders show the same information for the loaded user and for search results [#35244](https://github.com/woocommerce/woocommerce/pull/35244) +* Fix - Override filter_meta_data method, since it should be a no-op anyway. [#35192](https://github.com/woocommerce/woocommerce/pull/35192) +* Fix - Remove the direct dependency on `$_POST` when validating checkout data. [#35329](https://github.com/woocommerce/woocommerce/pull/35329) +* Fix - Revert change that auto collapses the product short description field. [#35213](https://github.com/woocommerce/woocommerce/pull/35213) +* Fix - Skip flaky settings API test [#35338](https://github.com/woocommerce/woocommerce/pull/35338) +* Fix - Update Playwright from 1.26.1 to 1.27.1 [#35106](https://github.com/woocommerce/woocommerce/pull/35106) +* Fix - When the minimum and maximum quantity are identical, render the quantity input and set it to disabled. [#34282](https://github.com/woocommerce/woocommerce/pull/34282) +* Add - Add "Empty Trash" functionality to HPOS list table. [#35489](https://github.com/woocommerce/woocommerce/pull/35489) +* Add - Add add attribute modal to the attribute field in the new product management MVP [#34999](https://github.com/woocommerce/woocommerce/pull/34999) +* Add - Add add new option for the category dropdown within the product MVP [#35132](https://github.com/woocommerce/woocommerce/pull/35132) +* Add - Add contextual product more menu [#35447](https://github.com/woocommerce/woocommerce/pull/35447) +* Add - Added a guided tour for WooCommerce Extensions page [#35278](https://github.com/woocommerce/woocommerce/pull/35278) +* Add - Added npm script for Playwright API Core Tests [#35283](https://github.com/woocommerce/woocommerce/pull/35283) +* Add - Added states for Senegal. [#35199](https://github.com/woocommerce/woocommerce/pull/35199) +* Add - Added the "Tour the WooCommerce Marketplace" task to onboarding tasks list [#35278](https://github.com/woocommerce/woocommerce/pull/35278) +* Add - Added Ukrainian subdivisions. [#35493](https://github.com/woocommerce/woocommerce/pull/35493) +* Add - Adding attribute edit modal for new product screen. [#35269](https://github.com/woocommerce/woocommerce/pull/35269) +* Add - Add manual stock management section to product management experience [#35047](https://github.com/woocommerce/woocommerce/pull/35047) +* Add - Add new Category dropdown field to the new Product Management screen. [#34400](https://github.com/woocommerce/woocommerce/pull/34400) +* Add - add new track events for in-app marketplace tour [#35278](https://github.com/woocommerce/woocommerce/pull/35278) +* Add - Add option and modal to create new attribute terms within MVP attribute modal. [#35131](https://github.com/woocommerce/woocommerce/pull/35131) +* Add - Add placeholder to description field [#35286](https://github.com/woocommerce/woocommerce/pull/35286) +* Add - Add playwright api-core-tests for data crud operations [#35347](https://github.com/woocommerce/woocommerce/pull/35347) +* Add - Add playwright api-core-tests for payment gateways crud operations [#35279](https://github.com/woocommerce/woocommerce/pull/35279) +* Add - Add playwright api-core-tests for product reviews crud operations [#35163](https://github.com/woocommerce/woocommerce/pull/35163) +* Add - Add playwright api-core-tests for product variations crud operations [#35355](https://github.com/woocommerce/woocommerce/pull/35355) +* Add - Add playwright api-core-tests for reports crud operations [#35388](https://github.com/woocommerce/woocommerce/pull/35388) +* Add - Add playwright api-core-tests for settingss crud operations [#35253](https://github.com/woocommerce/woocommerce/pull/35253) +* Add - Add playwright api-core-tests for system status crud operations [#35254](https://github.com/woocommerce/woocommerce/pull/35254) +* Add - Add playwright api-core-tests for webhooks crud operations [#35292](https://github.com/woocommerce/woocommerce/pull/35292) +* Add - Add Product description title in old editor for clarification. [#35154](https://github.com/woocommerce/woocommerce/pull/35154) +* Add - Add product inventory advanced section [#35164](https://github.com/woocommerce/woocommerce/pull/35164) +* Add - Add product management description to new product management experience [#34961](https://github.com/woocommerce/woocommerce/pull/34961) +* Add - Add product state badge to product form header [#35460](https://github.com/woocommerce/woocommerce/pull/35460) +* Add - Add product title to header when available [#35431](https://github.com/woocommerce/woocommerce/pull/35431) +* Add - Add scheduled sale support to new product edit page. [#34538](https://github.com/woocommerce/woocommerce/pull/34538) +* Add - Adds new Inbox Note to provide more information about WooCommerce Payments to users who dismiss the WCPay promo but say that they want more information in the exit survey. [#35581](https://github.com/woocommerce/woocommerce/pull/35581) +* Add - Add summary to new product page experience [#35201](https://github.com/woocommerce/woocommerce/pull/35201) +* Add - Include order datastore information in status report. [#35487](https://github.com/woocommerce/woocommerce/pull/35487) +* Add - Make it possible to add custom bulk action handling to the admin order list screen (when HPOS is enabled). [#35442](https://github.com/woocommerce/woocommerce/pull/35442) +* Add - Set In-App Marketplace Tour as completed on tour close [#35278](https://github.com/woocommerce/woocommerce/pull/35278) +* Add - When custom order tables are not authoritative, admin UI requests will be redirected to the matching legacy order screen as appropriate. [#35463](https://github.com/woocommerce/woocommerce/pull/35463) +* Update - Woo Blocks 8.9.2 [#35805](https://github.com/woocommerce/woocommerce/pull/35805) +* Update - Comment: Update WooCommerce Blocks to 8.7.2 [#35101](https://github.com/woocommerce/woocommerce/pull/35101) +* Update - Comment: Update WooCommerce Blocks to 8.7.3 [#35219](https://github.com/woocommerce/woocommerce/pull/35219) +* Update - Comment: Update WooCommerce Blocks to 8.9.1 [#35564](https://github.com/woocommerce/woocommerce/pull/35564) +* Update - CustomOrdersTableController::custom_orders_table_usage_is_enabled returns now false if the HPOS feature is disabled [#35597](https://github.com/woocommerce/woocommerce/pull/35597) +* Update - Disable inventory stock toggle when product stock management is disabled [#35059](https://github.com/woocommerce/woocommerce/pull/35059) +* Update - Improve the loading time of WooCommerce setup widget for large databases [#35334](https://github.com/woocommerce/woocommerce/pull/35334) +* Update - Permit showing a guided tour for WooCommerce Extensions page on desktops only [#35278](https://github.com/woocommerce/woocommerce/pull/35278) +* Update - Remove adding and managing products note [#35319](https://github.com/woocommerce/woocommerce/pull/35319) +* Update - Remove first downloadable product note [#35318](https://github.com/woocommerce/woocommerce/pull/35318) +* Update - Remove InsightFirstProductAndPayment note [#35309](https://github.com/woocommerce/woocommerce/pull/35309) +* Update - Remove insight on first sale note [#35341](https://github.com/woocommerce/woocommerce/pull/35341) +* Update - Remove manage store activity note [#35320](https://github.com/woocommerce/woocommerce/pull/35320) +* Update - Remove Popover.Slot usage and make use of exported SelectControlMenuSlot. [#35353](https://github.com/woocommerce/woocommerce/pull/35353) +* Update - Remove update store details note [#35322](https://github.com/woocommerce/woocommerce/pull/35322) +* Update - Update Array checks in playwright api-core-tests as some of the existing tests would produce false positives [#35462](https://github.com/woocommerce/woocommerce/pull/35462) +* Update - Update playwright api-core-tests for shipping crud operations [#35332](https://github.com/woocommerce/woocommerce/pull/35332) +* Update - Update playwright api-core-tests to execute for both base test environment and base JN environment with WooCommerce [#35522](https://github.com/woocommerce/woocommerce/pull/35522) +* Update - Update products task list UI [#35611](https://github.com/woocommerce/woocommerce/pull/35611) +* Update - Update ShippingLabelBanner add_meta_box action to only trigger on shop_order pages and remove deprecated function call. [#35212](https://github.com/woocommerce/woocommerce/pull/35212) +* Update - Update WooCommerce Blocks to 8.9.0 [#35521](https://github.com/woocommerce/woocommerce/pull/35521) +* Dev - Add variation price shortcut [#34948](https://github.com/woocommerce/woocommerce/pull/34948) +* Dev - Cleanup and deprecate unused Task properties and methods [#35450](https://github.com/woocommerce/woocommerce/pull/35450) +* Dev - Enable Playwright tests on Daily Smoke Test workflow and upload its Allure reports to S3 bucket. [#35114](https://github.com/woocommerce/woocommerce/pull/35114) +* Dev - Move product action buttons to header menu [#35214](https://github.com/woocommerce/woocommerce/pull/35214) +* Dev - Revert the changes introduced in PR #35282 [#35337](https://github.com/woocommerce/woocommerce/pull/35337) +* Dev - Show a dismissible snackbar if the server responds with an error [#35160](https://github.com/woocommerce/woocommerce/pull/35160) +* Dev - Update api-core-tests readme for consistency with new command and updates to other commands too. [#35303](https://github.com/woocommerce/woocommerce/pull/35303) +* Dev - Updated the COT plugin URL now that this feature can be enabled in a different way. [#34990](https://github.com/woocommerce/woocommerce/pull/34990) +* Dev - Update the list of tags for WC plugin on .org [#35573](https://github.com/woocommerce/woocommerce/pull/35573) +* Dev - Update unit test install script for db sockets. [#35152](https://github.com/woocommerce/woocommerce/pull/35152) +* Dev - Use plugins/woocommerce/tests/e2e-pw folder for saving test outputs [#35206](https://github.com/woocommerce/woocommerce/pull/35206) +* Dev - Uses the globa-setup.js to setup permalinks structure [#35282](https://github.com/woocommerce/woocommerce/pull/35282) +* Tweak - Move HPOS hook woocommerce_before_delete_order before deleting order. [#35517](https://github.com/woocommerce/woocommerce/pull/35517) +* Tweak - Adds new filter `woocommerce_get_customer_payment_tokens_limit` to set limit on number of payment methods fetched within the My Account page. [#29850](https://github.com/woocommerce/woocommerce/pull/29850) +* Tweak - Add source parameter for calls to the subscriptions endpoint on WooCommerce.com [#35051](https://github.com/woocommerce/woocommerce/pull/35051) +* Tweak - Fix @version header in form-login.php [#35479](https://github.com/woocommerce/woocommerce/pull/35479) +* Tweak - Move HPOS hook woocommerce_before_delete_order before deleting order. [#35517](https://github.com/woocommerce/woocommerce/pull/35517) +* Tweak - typo fix [#35111](https://github.com/woocommerce/woocommerce/pull/35111) +* Tweak - Unwrap product page input props and pass via getInputProps [#35034](https://github.com/woocommerce/woocommerce/pull/35034) +* Tweak - Updates the currency symbol used for the Azerbaijani manat. [#30605](https://github.com/woocommerce/woocommerce/pull/30605) +* Tweak - Use new Tooltip component instead of EnrichedLabel [#35024](https://github.com/woocommerce/woocommerce/pull/35024) +* Enhancement - Change the product info section title to Product Details [#35255](https://github.com/woocommerce/woocommerce/pull/35255) +* Enhancement - Fix the display of letter descenders in the shipping class dropdown menu [#35258](https://github.com/woocommerce/woocommerce/pull/35258) +* Enhancement - Improve the communication around required and optional [#35266](https://github.com/woocommerce/woocommerce/pull/35266) +* Enhancement - Increase the spacing between the shipping box illustration and the dimensions fields [#35259](https://github.com/woocommerce/woocommerce/pull/35259) +* Enhancement - Optimize query usage in the Onboarding tasks [#35065](https://github.com/woocommerce/woocommerce/pull/35065) +* Enhancement - Remove some placeholder values [#35267](https://github.com/woocommerce/woocommerce/pull/35267) +* Enhancement - Replace the trash can icon in the attribute list [#35133](https://github.com/woocommerce/woocommerce/pull/35133) +* Enhancement - Select the current new added shipping class [#35123](https://github.com/woocommerce/woocommerce/pull/35123) +* Enhancement - Tweaks the PR template for GitHub pull requests [#34597](https://github.com/woocommerce/woocommerce/pull/34597) + + += 7.1.1 2022-12-07 = + +**WooCommerce** + +* Patch - Move HPOS hook woocommerce_before_delete_order before deleting order. [#35517](https://github.com/woocommerce/woocommerce/pull/35517) + +**WooCommerce Blocks 8.7.6** + +* Fix - Mini Cart block: fix compatibility with Page Optimize and Product Bundles plugins. [#7794](https://github.com/woocommerce/woocommerce-blocks/pull/7794) +* Fix - Mini Cart block: Load wc-blocks-registry package at the page's load instead of lazy load it. [#7813](https://github.com/woocommerce/woocommerce-blocks/pull/7813) + += 7.1.0 2022-11-08 = + +**WooCommerce** + +* Fix - Fix business details step when Gutenberg is active [#35448](https://github.com/woocommerce/woocommerce/pull/35448) +* Fix - Check order type is set before returning to prevent notice. [#35349](https://github.com/woocommerce/woocommerce/pull/35349) +* Fix - When HPOS is enabled, posts are authoritative, and sync is enabled, ensure the HPOS record correctly tracks the CPT order record. [#35402](https://github.com/woocommerce/woocommerce/pull/35402) +* Fix - Allow line breaks in order note again. [#35366](https://github.com/woocommerce/woocommerce/pull/35366) +* Fix - Sync orders for stats table. [#35118](https://github.com/woocommerce/woocommerce/pull/35118) +* Fix - Fix (un)trashing of orders when using HPOS [#35125](https://github.com/woocommerce/woocommerce/pull/35125) +* Fix - Check whether order has classname before returning. [#35207](https://github.com/woocommerce/woocommerce/pull/35207) +* Fix - Add billing and shipping address indexes on order update. [#35121](https://github.com/woocommerce/woocommerce/pull/35121) +* Fix - Use correct datastore when backfilling orders. [#35176](https://github.com/woocommerce/woocommerce/pull/35176) +* Fix - (HPOS) Ensure we use GMT when populating the `date_created_gmt` column for orders. [#34875](https://github.com/woocommerce/woocommerce/pull/34875) +* Fix - Admin list table for orders (in HPOS mode) should check in case the user pages beyond the available range. [#34793](https://github.com/woocommerce/woocommerce/pull/34793) +* Fix - Allow features to declare their initial enabled state. [#34867](https://github.com/woocommerce/woocommerce/pull/34867) +* Fix - Customers should be able to pay for orders so long as any required stock reductions have already taken place. [#33575](https://github.com/woocommerce/woocommerce/pull/33575) +* Fix - Do no override order defaults with NULL values (HPOS) [#34822](https://github.com/woocommerce/woocommerce/pull/34822) +* Fix - Fix "Industry" options fails to save in the Industry step after reloading the page for OBW [#34847](https://github.com/woocommerce/woocommerce/pull/34847) +* Fix - Fix a fatal error thrown by init_theorder_object due to the return type declaration [#34730](https://github.com/woocommerce/woocommerce/pull/34730) +* Fix - fixed mismatching jetpack user should not see mobile app task list item [#35052](https://github.com/woocommerce/woocommerce/pull/35052) +* Fix - Fix enable guided mode button not trigger when its text is translated [#34843](https://github.com/woocommerce/woocommerce/pull/34843) +* Fix - Fixes test environment setup setting datetime for customer user creation [#34888](https://github.com/woocommerce/woocommerce/pull/34888) +* Fix - Fix JSON schema for product's image properties. [#34852](https://github.com/woocommerce/woocommerce/pull/34852) +* Fix - Fix obw validation issue to truly disable the continue buttons [#34895](https://github.com/woocommerce/woocommerce/pull/34895) +* Fix - Fix onboarding wizard popover padding for WP6.1 [#34896](https://github.com/woocommerce/woocommerce/pull/34896) +* Fix - Fix order refund removal when the HPOS datastore is in use. [#34785](https://github.com/woocommerce/woocommerce/pull/34785) +* Fix - Handle loading and error states for magic link button [#35068](https://github.com/woocommerce/woocommerce/pull/35068) +* Fix - Implement missing method of calculating shipping and total tax. [#34805](https://github.com/woocommerce/woocommerce/pull/34805) +* Fix - Serialize meta value before rendering so that it's rendered properly. [#34952](https://github.com/woocommerce/woocommerce/pull/34952) +* Fix - Set correct timezone when backfilling data. [#35033](https://github.com/woocommerce/woocommerce/pull/35033) +* Add - Twenty Twenty-Three theme compatibility. [#35306](https://github.com/woocommerce/woocommerce/pull/35306) +* Add - Add handling for plugin-feature incompatibilities [#34879](https://github.com/woocommerce/woocommerce/pull/34879) +* Add - Add inventory stock management to new product management experience [#34984](https://github.com/woocommerce/woocommerce/pull/34984) +* Add - Add new attributes section and field for the new Product Management Experience. [#34751](https://github.com/woocommerce/woocommerce/pull/34751) +* Add - Add order preview functionality to HPOS list table. [#34770](https://github.com/woocommerce/woocommerce/pull/34770) +* Add - Add playwright api-core-tests for customers crud operations [#34945](https://github.com/woocommerce/woocommerce/pull/34945) +* Add - Add playwright api-core-tests for order notes crud operations [#34979](https://github.com/woocommerce/woocommerce/pull/34979) +* Add - Add playwright api-core-tests for product properties crud operations [#34998](https://github.com/woocommerce/woocommerce/pull/34998) +* Add - Add playwright api-core-tests for tax rates crud operations [#34960](https://github.com/woocommerce/woocommerce/pull/34960) +* Add - Add shipping class section and dropdown [#34684](https://github.com/woocommerce/woocommerce/pull/34684) +* Add - Add shipping dimensions section to product page #34329 [#34856](https://github.com/woocommerce/woocommerce/pull/34856) +* Add - Add SKU field to new product management experience [#34978](https://github.com/woocommerce/woocommerce/pull/34978) +* Add - Add the WooCommerce features engine [#34727](https://github.com/woocommerce/woocommerce/pull/34727) +* Add - Deprecate existing `wp wc cot migrate` command and replace with `wp wc cot sync`. [#34676](https://github.com/woocommerce/woocommerce/pull/34676) +* Add - Disable action buttons when product form is invalid [#34658](https://github.com/woocommerce/woocommerce/pull/34658) +* Add - Expand attributes list to display attributes list and allow removal and re-ordering. [#34841](https://github.com/woocommerce/woocommerce/pull/34841) +* Add - Images Product management [#34769](https://github.com/woocommerce/woocommerce/pull/34769) +* Add - Improve on feature incompatibility plugin screens. [#35063](https://github.com/woocommerce/woocommerce/pull/35063) +* Add - Render columns via action so that they can be hooked into. [#34900](https://github.com/woocommerce/woocommerce/pull/34900) +* Add - Support `wc_customer_bought_product` function in HPOS. [#34931](https://github.com/woocommerce/woocommerce/pull/34931) +* Add - The updates will mean that the github workflows use the playwright versions of the api-core-tests rather than the supertest versions of the tests. [#34935](https://github.com/woocommerce/woocommerce/pull/34935) +* Update - Don't show feature compatibility warnings for inactive plugins [#35333](https://github.com/woocommerce/woocommerce/pull/35333) +* Update - Update WooCommerce Blocks to 8.7.5 [#35428](https://github.com/woocommerce/woocommerce/pull/35428) +* Update - Improve the warnings about incompatibilities between plugins and features [#35198](https://github.com/woocommerce/woocommerce/pull/35198) +* Update - Additional payment methods on new WCPay promotion page (payment-welcome) [#34581](https://github.com/woocommerce/woocommerce/pull/34581) +* Update - Add Tiktok to free grow extensions list [#34953](https://github.com/woocommerce/woocommerce/pull/34953) +* Update - Allowing generic item type in new experimental SelectControl. [#34547](https://github.com/woocommerce/woocommerce/pull/34547) +* Update - Change order data store internal key to props for better representation. [#34627](https://github.com/woocommerce/woocommerce/pull/34627) +* Update - Changing inbox display to only 5 notes with the ability to load more. [#35003](https://github.com/woocommerce/woocommerce/pull/35003) +* Update - Deploy spotlight product tour treatment [#34859](https://github.com/woocommerce/woocommerce/pull/34859) +* Update - Track orders origin in WC_Tracker. [#35069](https://github.com/woocommerce/woocommerce/pull/35069) +* Update - Updates a few css selectors to be more robust [#34790](https://github.com/woocommerce/woocommerce/pull/34790) +* Update - Update WCPay promo requirements and ensure it's dismissed on every scenario [#35030](https://github.com/woocommerce/woocommerce/pull/35030) +* Dev - Add api-core-tests for playwright [#34835](https://github.com/woocommerce/woocommerce/pull/34835) +* Dev - Add fail-fast configuration to Playwright E2E tests. [#33977](https://github.com/woocommerce/woocommerce/pull/33977) +* Dev - Add new shippping class modal to a shipping class section in product page [#34937](https://github.com/woocommerce/woocommerce/pull/34937) +* Dev - Add shipping dimensions image to visualize the sizes of the product #34329 [#34857](https://github.com/woocommerce/woocommerce/pull/34857) +* Dev - Add tests for UI Revamp on Marketing Page. [#34840](https://github.com/woocommerce/woocommerce/pull/34840) +* Dev - Exclude "debug" module from babel compile to fix the tour kit stories loading error [#34831](https://github.com/woocommerce/woocommerce/pull/34831) +* Dev - Fix node and pnpm versions via engines [#34773](https://github.com/woocommerce/woocommerce/pull/34773) +* Dev - Improve the matching of plugins during the compatibility check. [#35070](https://github.com/woocommerce/woocommerce/pull/35070) +* Dev - Load size units to show it as a suffix of shipping dimensions fields #34329 [#34856](https://github.com/woocommerce/woocommerce/pull/34856) +* Dev - Match TypeScript version with syncpack [#34787](https://github.com/woocommerce/woocommerce/pull/34787) +* Dev - set the store country in the test step [#34972](https://github.com/woocommerce/woocommerce/pull/34972) +* Dev - Update Playwright to 1.26.0 and fix a few flaky tests [#34790](https://github.com/woocommerce/woocommerce/pull/34790) +* Dev - Update pnpm version constraint to 7.13.3 to avoid auto-install-peers issues [#35007](https://github.com/woocommerce/woocommerce/pull/35007) +* Tweak - Add hooks that fire before an HPOS order is deleted or trashed. [#34858](https://github.com/woocommerce/woocommerce/pull/34858) +* Tweak - Disable new-product-management-experience feature flag in development. [#34836](https://github.com/woocommerce/woocommerce/pull/34836) +* Tweak - Update copy in the payments welcome modal [#35031](https://github.com/woocommerce/woocommerce/pull/35031) +* Tweak - Update subdivision codes for New Zealand, to match current CLDR specification. [#35011](https://github.com/woocommerce/woocommerce/pull/35011) +* Tweak - When the primary order store is the posts table, and sync is enabled, propagate changes outside of dedicated migrations. [#34863](https://github.com/woocommerce/woocommerce/pull/34863) +* Performance - Support fetching order types in bulk to improve performance. [#34976](https://github.com/woocommerce/woocommerce/pull/34976) +* Enhancement - Add support for complex field queries for orders. [#34533](https://github.com/woocommerce/woocommerce/pull/34533) +* Enhancement - Also read from posts when reading from orders as a mittigation to direct write. [#34465](https://github.com/woocommerce/woocommerce/pull/34465) +* Enhancement - Enable async typeahead fields for the attribute and term fields within products. [#34744](https://github.com/woocommerce/woocommerce/pull/34744) +* Enhancement - Enchance tour experience for store location [#34697](https://github.com/woocommerce/woocommerce/pull/34697) + +**WooCommerce Blocks 8.7.0 & 8.7.1 & 8.7.2 & 8.7.3 & 8.7.4 & 8.7.5** + +* Enhancement - Improve visual consistency between block links. ([7340](https://github.com/woocommerce/woocommerce-blocks/pull/7340)) +* Enhancement - Update the titles of some inner blocks of the Cart block and remove the lock of the Cross-Sells parent block. ([7232](https://github.com/woocommerce/woocommerce-blocks/pull/7232)) +* Enhancement - Add filter for place order button label. ([7154](https://github.com/woocommerce/woocommerce-blocks/pull/7154)) +* Enhancement - Exposed data related to the checkout through wordpress/data stores. ([6612](https://github.com/woocommerce/woocommerce-blocks/pull/6612)) +* Enhancement - Add simple, large & two menus footer patterns. ([7306](https://github.com/woocommerce/woocommerce-blocks/pull/7306)) +* Enhancement - Add minimal, large, and essential header patterns. ([7292](https://github.com/woocommerce/woocommerce-blocks/pull/7292)) +* Enhancement - Add `showRemoveItemLink` as a new checkout filter to allow extensions to toggle the visibility of the `Remove item` button under each cart line item. ([7242](https://github.com/woocommerce/woocommerce-blocks/pull/7242)) +* Enhancement - Add support for a GT tracking ID for Google Analytics. ([7213](https://github.com/woocommerce/woocommerce-blocks/pull/7213)) +* Enhancement - Separate filter titles and filter controls by converting filter blocks to use Inner Blocks. ([6978](https://github.com/woocommerce/woocommerce-blocks/pull/6978)) +* Enhancement - StoreApi requests will return a `Cart-Token` header that can be used to retrieve the cart from the corresponding session via **GET** `/wc/store/v1/cart`. ([5953](https://github.com/woocommerce/woocommerce-blocks/pull/5953)) +* Fix - Fixed HTML rendering in description of active payment integrations. ([7313](https://github.com/woocommerce/woocommerce-blocks/pull/7313)) +* Fix - Hide the shipping address form from the Checkout when the "Force shipping to the customer billing address" is enabled. ([7268](https://github.com/woocommerce/woocommerce-blocks/pull/7268)) +* Fix - Fixed an error where adding new pages would cause an infinite loop and large amounts of memory use in redux. ([7256](https://github.com/woocommerce/woocommerce-blocks/pull/7256)) +* Fix - Ensure error messages containing HTML are shown correctly in the Cart and Checkout blocks. ([7231](https://github.com/woocommerce/woocommerce-blocks/pull/7231)) +* Fix - Prevent locked inner blocks from sometimes displaying twice. ([6676](https://github.com/woocommerce/woocommerce-blocks/pull/6676)) +* Fix - StoreApi `/checkout` endpoint now returns HTTP 402 instead of HTTP 400 when payment fails. ([7273](https://github.com/woocommerce/woocommerce-blocks/pull/7273)) +* Fix - Fix a problem that causes an infinite loop when inserting Cart block in wordpress.com. ([7367](https://github.com/woocommerce/woocommerce-blocks/pull/7367)) +* Fix - Fixed an issue where JavaScript errors would occur when more than one extension tried to filter specific payment methods in the Cart and Checkout blocks. ([7377](https://github.com/woocommerce/woocommerce-blocks/pull/7377)) +* Fix - Fixed a problem where Custom Order Tables compatibility declaration could fail due to the unpredictable plugin order load. ([7395](https://github.com/woocommerce/woocommerce-blocks/pull/7395)) +* Fix - Refactor useCheckoutAddress hook to enable "Use same address for billing" option in Editor ([7393](https://github.com/woocommerce/woocommerce-blocks/pull/7393)) +* Fix - Fixed an issue where the argument passed to `canMakePayment` contained the incorrect keys. Also fixed the current user's customer data appearing in the editor when editing the Checkout block. +* Fix - Compatibility fix for Cart and Checkout inner blocks for WordPress 6.1. + += 7.0.1 2022-11-01 = + +**WooCommerce** + +* Dev - Twenty Twenty-Three theme compatibility. [#35306](https://github.com/woocommerce/woocommerce/pull/35306) +* Dev - Simplify and reduce size of payload supplied by the woocommerce_get_customer_details ajax endpoint. + +**WooCommerce Blocks 8.5.2** + +* Enhancement - Fix Mini Cart Global Styles. [7515](https://github.com/woocommerce/woocommerce-blocks/pull/7515) +* Enhancement - Fix inconsistent button styling with TT3. ([7516](https://github.com/woocommerce/woocommerce-blocks/pull/7516)) +* Enhancement - Make the Filter by Price block range color dependent of the theme color. [7525](https://github.com/woocommerce/woocommerce-blocks/pull/7525) +* Enhancement - Filter by Price block: fix price slider visibility on dark themes. [7527](https://github.com/woocommerce/woocommerce-blocks/pull/7527) +* Enhancement - Update the Mini Cart block drawer to honor the theme's background. [7510](https://github.com/woocommerce/woocommerce-blocks/pull/7510) +* Enhancement - Add white background to Filter by Attribute block dropdown so text is legible in dark backgrounds. [7506](https://github.com/woocommerce/woocommerce-blocks/pull/7506) + = 7.0.0 2022-10-11 = **WooCommerce** diff --git a/changelog/e2e-fix-allure-upload-input b/changelog/e2e-fix-allure-upload-input new file mode 100644 index 00000000000..3ff1edec50d --- /dev/null +++ b/changelog/e2e-fix-allure-upload-input @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix the incorrect workflow input for uploading Allure reports. diff --git a/changelog/pr-36705 b/changelog/pr-36705 new file mode 100644 index 00000000000..c8308e99213 --- /dev/null +++ b/changelog/pr-36705 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Added `woocommerce_widget_layered_nav_filters_start/end` hooks around layered nav filters widget \ No newline at end of file diff --git a/package.json b/package.json index 221c042fa0a..9c8ac22039e 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,10 @@ "title": "WooCommerce Monorepo", "description": "Monorepo for the WooCommerce ecosystem", "homepage": "https://woocommerce.com/", + "engines": { + "node": "^16.14.1", + "pnpm": "^8.3.1" + }, "private": true, "repository": { "type": "git", @@ -13,53 +17,64 @@ "bugs": { "url": "https://github.com/woocommerce/woocommerce/issues" }, + "bin": { + "utils": "./tools/monorepo-utils/bin/run" + }, "scripts": { "build": "pnpm exec turbo run turbo:build", "test": "pnpm exec turbo run turbo:test", + "clean": "pnpm store prune && git clean -fx **/node_modules && pnpm i", "preinstall": "npx only-allow pnpm", "postinstall": "pnpm git:update-hooks", "git:update-hooks": "rm -r .git/hooks && mkdir -p .git/hooks && husky install", "create-extension": "node ./tools/create-extension/index.js", "cherry-pick": "node ./tools/cherry-pick/bin/run", - "sync-dependencies": "pnpm exec syncpack -- fix-mismatches" + "sync-dependencies": "pnpm exec syncpack -- fix-mismatches", + "utils": "./tools/monorepo-utils/bin/run" }, "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": "^6.15.0", + "@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": "^3.0.0", - "prettier": "npm:wp-prettier@^2.2.1-beta-1", - "regenerator-runtime": "^0.13.9", + "postcss-loader": "^4.3.0", + "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", - "syncpack": "^8.2.4", - "turbo": "^1.4.5", - "typescript": "^4.8.3", + "sass": "^1.59.3", + "sass-loader": "^10.4.1", + "syncpack": "^9.8.4", + "turbo": "^1.9.3", + "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" + }, + "pnpm": { + "overrides": { + "@types/react": "^17.0.2", + "react": "^17.0.2" + } } } diff --git a/packages/js/README.md b/packages/js/README.md index 5f82f22a152..5c7dfe15136 100644 --- a/packages/js/README.md +++ b/packages/js/README.md @@ -1,6 +1,6 @@ # WooCommerce Packages -Currently we have a small set of public-facing packages that can be dowloaded from [npm](https://www.npmjs.com/org/woocommerce) and used in external applications. +Currently we have a set of public-facing packages that can be dowloaded from [npm](https://www.npmjs.com/org/woocommerce) and used in external applications. Here is a non-exhaustive list. - `@woocommerce/components`: A library of components that can be used to create pages in the WooCommerce dashboard and reports pages. - `@woocommerce/csv-export`: A set of functions to convert data into CSV values, and enable a browser download of the CSV data. @@ -12,12 +12,11 @@ Currently we have a small set of public-facing packages that can be dowloaded fr ## Working with existing packages - You can make changes to packages files as normal, and running `pnpm start` will compile and watch both app files and packages. -- :warning: Make sure any dependencies you add to a package are also added to that package's `package.json`, not just the woocommerce-admin package.json -- :warning: Make sure you're not importing from any woocommerce-admin files outside of the package (you can import from other packages, just use the `import from @woocommerce/package` syntax). -- Add your change to the CHANGELOG for that package under the next version number, creating one if necessary (we use semantic versioning for packages, [see these guidelines](https://github.com/WordPress/gutenberg/blob/master/CONTRIBUTING.md#maintaining-changelogs)). +- :warning: Add any dependencies to a package using `pnpm add` from the package root. +- :warning: Make sure you're not importing from any other files outside of the package (you can import from other packages, just use the `import from @woocommerce/package` syntax). - Don't change the version in `package.json`. - Label your PR with the `Packages` label. -- Once merged, you can wait for the next package release roundup, or you can publish a release now (see below, "Publishing packages"). +- See the [Package Release Tool](https://github.com/woocommerce/woocommerce/blob/f9e7a5a3fb11cdd4dc064c02e045cf429cb6a2b6/tools/package-release/README.md) for instructions on how to release packages. --- @@ -36,7 +35,7 @@ To create a new package, add a new folder to `/packages`, containing… "author": "Automattic", "license": "GPL-2.0-or-later", "keywords": [ "wordpress", "woocommerce" ], - "homepage": "https://github.com/woocommerce/woocommerce/tree/main/packages/[_YOUR_PACKAGE_]/README.md", + "homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/[_YOUR_PACKAGE_]/README.md", "repository": { "type": "git", "url": "https://github.com/woocommerce/woocommerce.git" @@ -61,25 +60,14 @@ To create a new package, add a new folder to `/packages`, containing… - Package description - Installation details - Usage example -4. A `src` directory for the source of your module, which will be built by default using the `pnpm run build:packages` command. Note that you'll want an `index.js` file that exports the package contents, see other packages for examples. +4. A `src` directory for the source of your module, which will be built by default using the `pnpm run turbo:build` command. Note that you'll want an `index.js` file that exports the package contents, see other packages for examples. -5. Add the new package name to `packages/dependency-extraction-webpack-plugin/assets/packages.js` so that users of that plugin will also be able to use the new package without enqueuing it. +5. A blank Changelog file, `changelog.md`. ---- +``` +# Changelog -## Publishing packages +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +``` -- Run `pnpm run publish-packages:check` to run pnpm publish with the `--dry-run` option -- Create a PR with a CHANGELOG for each updated package (or try to add to the CHANGELOG with any PR editing `packages/`) -- Run `pnpm run publish-packages:prod` to publish the package -- _OR_ Run `pnpm run publish-packages:dev` to publish "next" releases (installed as `pnpm i @woocommerce/package@next`). Only use `:dev` if you have a reason to. -- Both commands will run `build:packages` before the publishing task, just to catch any last updates. - -### Publishing a single package - -Sometimes, its helpful to release a singular package. This can be done directly through pnpm. Be sure versions and builds are correct. - -- Bump the version in the package's package.json as well as its CHANGELOG file. -- `pnpm install && pnpm run build:packages` to build packages. -- `cd packages/` -- `pnpm publish` +6. Add the new package name to `packages/js/dependency-extraction-webpack-plugin/assets/packages.js` so that users of that plugin will also be able to use the new package without enqueuing it. diff --git a/packages/js/admin-e2e-tests/changelog/dev-adjust-sync b/packages/js/admin-e2e-tests/changelog/dev-adjust-sync new file mode 100644 index 00000000000..f11d1e352f4 --- /dev/null +++ b/packages/js/admin-e2e-tests/changelog/dev-adjust-sync @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Dev dependency bump + + diff --git a/packages/js/admin-e2e-tests/changelog/dev-consolidate-eslint-versions b/packages/js/admin-e2e-tests/changelog/dev-consolidate-eslint-versions new file mode 100644 index 00000000000..d3d95c39119 --- /dev/null +++ b/packages/js/admin-e2e-tests/changelog/dev-consolidate-eslint-versions @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update eslint to 8.32.0 across the monorepo. diff --git a/packages/js/admin-e2e-tests/changelog/dev-pin-wp-deps-6 b/packages/js/admin-e2e-tests/changelog/dev-pin-wp-deps-6 new file mode 100644 index 00000000000..f424506bcb2 --- /dev/null +++ b/packages/js/admin-e2e-tests/changelog/dev-pin-wp-deps-6 @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Use syncpack to update dependencies. diff --git a/packages/js/admin-e2e-tests/changelog/dev-update-eslint-plugin b/packages/js/admin-e2e-tests/changelog/dev-update-eslint-plugin new file mode 100644 index 00000000000..03f6c25ee4c --- /dev/null +++ b/packages/js/admin-e2e-tests/changelog/dev-update-eslint-plugin @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Lint fixes diff --git a/packages/js/admin-e2e-tests/changelog/dev-update-pnpm8 b/packages/js/admin-e2e-tests/changelog/dev-update-pnpm8 new file mode 100644 index 00000000000..c21e013f454 --- /dev/null +++ b/packages/js/admin-e2e-tests/changelog/dev-update-pnpm8 @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Update pnpm to version 8. diff --git a/packages/js/admin-e2e-tests/changelog/fix-typescript-incremental-builds b/packages/js/admin-e2e-tests/changelog/fix-typescript-incremental-builds new file mode 100644 index 00000000000..f2bdc9a96ae --- /dev/null +++ b/packages/js/admin-e2e-tests/changelog/fix-typescript-incremental-builds @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: TypeScript build change + + diff --git a/packages/js/admin-e2e-tests/changelog/fix-typescript-package-isolation b/packages/js/admin-e2e-tests/changelog/fix-typescript-package-isolation new file mode 100644 index 00000000000..2d087939231 --- /dev/null +++ b/packages/js/admin-e2e-tests/changelog/fix-typescript-package-isolation @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Configuration change only + + diff --git a/packages/js/admin-e2e-tests/changelog/remove-wcpay-accordion b/packages/js/admin-e2e-tests/changelog/remove-wcpay-accordion new file mode 100644 index 00000000000..a84afb86acd --- /dev/null +++ b/packages/js/admin-e2e-tests/changelog/remove-wcpay-accordion @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update showOtherPaymentMethods() to test latest payment task properly diff --git a/packages/js/admin-e2e-tests/changelog/update-bump-config b/packages/js/admin-e2e-tests/changelog/update-bump-config new file mode 100644 index 00000000000..c89120d3892 --- /dev/null +++ b/packages/js/admin-e2e-tests/changelog/update-bump-config @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Unify semver range for `config@3.3.7` (from `^3.3.7`). Fix `node_env_var_name is not defined` error. diff --git a/packages/js/admin-e2e-tests/changelog/update-changelogger b/packages/js/admin-e2e-tests/changelog/update-changelogger new file mode 100644 index 00000000000..1674c919e78 --- /dev/null +++ b/packages/js/admin-e2e-tests/changelog/update-changelogger @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: Dev dependency update. + + diff --git a/packages/js/admin-e2e-tests/changelog/update-remove-obw-theme-step b/packages/js/admin-e2e-tests/changelog/update-remove-obw-theme-step new file mode 100644 index 00000000000..4f26428d3c9 --- /dev/null +++ b/packages/js/admin-e2e-tests/changelog/update-remove-obw-theme-step @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Remove obw theme step tests diff --git a/packages/js/admin-e2e-tests/composer.json b/packages/js/admin-e2e-tests/composer.json index 90649ce14ac..6f20e363555 100644 --- a/packages/js/admin-e2e-tests/composer.json +++ b/packages/js/admin-e2e-tests/composer.json @@ -5,7 +5,7 @@ "license": "GPL-3.0-or-later", "minimum-stability": "dev", "require-dev": { - "automattic/jetpack-changelogger": "3.1.3" + "automattic/jetpack-changelogger": "3.3.0" }, "config": { "platform": { diff --git a/packages/js/admin-e2e-tests/composer.lock b/packages/js/admin-e2e-tests/composer.lock index 602ff6fabdc..f448cbf9e57 100644 --- a/packages/js/admin-e2e-tests/composer.lock +++ b/packages/js/admin-e2e-tests/composer.lock @@ -4,32 +4,32 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cae17ca18e2a2a6cefe200df88081346", + "content-hash": "959b38edbc3ae0c3853c02e86852f583", "packages": [], "packages-dev": [ { "name": "automattic/jetpack-changelogger", - "version": "v3.1.3", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/Automattic/jetpack-changelogger.git", - "reference": "cdd256d8ba6369f82d9377de7e9e2598e3e16ae0" + "reference": "8f63c829b8d1b0d7b1d5de93510d78523ed18959" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Automattic/jetpack-changelogger/zipball/cdd256d8ba6369f82d9377de7e9e2598e3e16ae0", - "reference": "cdd256d8ba6369f82d9377de7e9e2598e3e16ae0", + "url": "https://api.github.com/repos/Automattic/jetpack-changelogger/zipball/8f63c829b8d1b0d7b1d5de93510d78523ed18959", + "reference": "8f63c829b8d1b0d7b1d5de93510d78523ed18959", "shasum": "" }, "require": { "php": ">=5.6", - "symfony/console": "^3.4 || ^5.2", - "symfony/process": "^3.4 || ^5.2", + "symfony/console": "^3.4 || ^5.2 || ^6.0", + "symfony/process": "^3.4 || ^5.2 || ^6.0", "wikimedia/at-ease": "^1.2 || ^2.0" }, "require-dev": { "wikimedia/testing-access-wrapper": "^1.0 || ^2.0", - "yoast/phpunit-polyfills": "1.0.3" + "yoast/phpunit-polyfills": "1.0.4" }, "bin": [ "bin/changelogger" @@ -38,7 +38,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "3.1.x-dev" + "dev-trunk": "3.3.x-dev" }, "mirror-repo": "Automattic/jetpack-changelogger", "version-constants": { @@ -60,9 +60,9 @@ ], "description": "Jetpack Changelogger tool. Allows for managing changelogs by dropping change files into a changelog directory with each PR.", "support": { - "source": "https://github.com/Automattic/jetpack-changelogger/tree/v3.1.3" + "source": "https://github.com/Automattic/jetpack-changelogger/tree/v3.3.0" }, - "time": "2022-06-21T07:31:56+00:00" + "time": "2022-12-26T13:49:01+00:00" }, { "name": "psr/log", @@ -204,12 +204,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "6637e62480b60817b9a6984154a533e8e64c6bd5" + "reference": "1a692492190773c5310bc7877cb590c04c2f05be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/6637e62480b60817b9a6984154a533e8e64c6bd5", - "reference": "6637e62480b60817b9a6984154a533e8e64c6bd5", + "url": "https://api.github.com/repos/symfony/debug/zipball/1a692492190773c5310bc7877cb590c04c2f05be", + "reference": "1a692492190773c5310bc7877cb590c04c2f05be", "shasum": "" }, "require": { @@ -249,7 +249,7 @@ "description": "Provides tools to ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/debug/tree/v4.4.41" + "source": "https://github.com/symfony/debug/tree/v4.4.44" }, "funding": [ { @@ -266,7 +266,7 @@ } ], "abandoned": "symfony/error-handler", - "time": "2022-04-12T15:19:55+00:00" + "time": "2022-07-28T16:29:46+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -274,12 +274,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { @@ -295,7 +295,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -334,7 +334,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -350,7 +350,7 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/process", diff --git a/packages/js/admin-e2e-tests/package.json b/packages/js/admin-e2e-tests/package.json index 1db0d4e9689..e77af44be04 100644 --- a/packages/js/admin-e2e-tests/package.json +++ b/packages/js/admin-e2e-tests/package.json @@ -5,8 +5,8 @@ "description": "E2E tests for the new WooCommerce interface.", "homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/admin-e2e-tests/README.md", "engines": { - "node": "^16.13.1", - "pnpm": "^7.13.3" + "node": "^16.14.1", + "pnpm": "^8.3.1" }, "repository": { "type": "git", @@ -29,7 +29,7 @@ "@jest/globals": "^27.5.1", "@types/jest": "^27.4.1", "@woocommerce/e2e-utils": "workspace:*", - "config": "^3.3.7" + "config": "3.3.7" }, "peerDependencies": { "@woocommerce/e2e-environment": "^0.2.3 || ^0.3.0", @@ -41,27 +41,27 @@ "@types/config": "0.0.41", "@types/expect-puppeteer": "^4.4.7", "@types/puppeteer": "^5.4.5", - "@typescript-eslint/eslint-plugin": "^5.14.0", + "@typescript-eslint/eslint-plugin": "^5.54.0", "@woocommerce/api": "^0.2.0", "@woocommerce/eslint-plugin": "workspace:*", - "eslint": "^8.12.0", + "eslint": "^8.32.0", "jest": "^27.5.1", "jest-cli": "^27.5.1", "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" diff --git a/packages/js/admin-e2e-tests/src/pages/OnboardingWizard.ts b/packages/js/admin-e2e-tests/src/pages/OnboardingWizard.ts index 839350adb30..ea7c1941ece 100644 --- a/packages/js/admin-e2e-tests/src/pages/OnboardingWizard.ts +++ b/packages/js/admin-e2e-tests/src/pages/OnboardingWizard.ts @@ -14,7 +14,6 @@ import { StoreDetails, StoreDetailsSection, } from '../sections/onboarding/StoreDetailsSection'; -import { ThemeSection } from '../sections/onboarding/ThemeSection'; import { BasePage } from './BasePage'; export class OnboardingWizard extends BasePage { @@ -24,7 +23,6 @@ export class OnboardingWizard extends BasePage { industry: IndustrySection; productTypes: ProductTypeSection; business: BusinessSection; - themes: ThemeSection; constructor( page: Page ) { super( page ); @@ -32,7 +30,6 @@ export class OnboardingWizard extends BasePage { this.industry = new IndustrySection( page ); this.productTypes = new ProductTypeSection( page ); this.business = new BusinessSection( page ); - this.themes = new ThemeSection( page ); } async skipStoreSetup(): Promise< void > { @@ -90,7 +87,6 @@ export class OnboardingWizard extends BasePage { productNumber: string; currentlySelling: string; }; - themeTitle?: string; } = {} ): Promise< void > { await this.navigate(); @@ -142,13 +138,5 @@ export class OnboardingWizard extends BasePage { await this.business.uncheckAllRecommendedBusinessFeatures(); await this.continue(); - await this.themes.isDisplayed(); - - // This navigates to the home screen - if ( options.themeTitle ) { - await this.themes.continueWithTheme( options.themeTitle ); - } else { - await this.themes.continueWithActiveTheme(); - } } } diff --git a/packages/js/admin-e2e-tests/src/pages/PaymentsSetup.ts b/packages/js/admin-e2e-tests/src/pages/PaymentsSetup.ts index 8efc7dc4ed4..2404ff42000 100644 --- a/packages/js/admin-e2e-tests/src/pages/PaymentsSetup.ts +++ b/packages/js/admin-e2e-tests/src/pages/PaymentsSetup.ts @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { waitForElementByText, getElementByText } from '../utils/actions'; +import { waitForElementByText } from '../utils/actions'; import { BasePage } from './BasePage'; type PaymentMethodWithSetupButton = @@ -12,7 +12,7 @@ type PaymentMethodWithSetupButton = | 'mollie' | 'bacs'; -type PaymentMethod = PaymentMethodWithSetupButton | 'cod'; +// type PaymentMethod = PaymentMethodWithSetupButton | 'cod'; export class PaymentsSetup extends BasePage { url = 'wp-admin/admin.php?page=wc-admin&task=payments'; @@ -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' ); } diff --git a/packages/js/admin-e2e-tests/src/sections/onboarding/ThemeSection.ts b/packages/js/admin-e2e-tests/src/sections/onboarding/ThemeSection.ts deleted file mode 100644 index 8439fa01fc8..00000000000 --- a/packages/js/admin-e2e-tests/src/sections/onboarding/ThemeSection.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Internal dependencies - */ -import { BasePage } from '../../pages/BasePage'; -import { waitForElementByText } from '../../utils/actions'; - -export class ThemeSection extends BasePage { - async isDisplayed(): Promise< void > { - await waitForElementByText( 'h2', 'Choose a theme' ); - await waitForElementByText( 'button', 'All themes' ); - } - - async continueWithActiveTheme(): Promise< void > { - await this.clickButtonWithText( 'Continue with my active theme' ); - } - - async continueWithTheme( themeTitle: string ): Promise< void > { - const title = await waitForElementByText( 'h2', themeTitle ); - const chooseButton = await title?.evaluateHandle( ( element ) => { - const card = element.closest( '.components-card' ); - return Array.from( card?.querySelectorAll( 'button' ) || [] ).find( - ( el ) => el.textContent === 'Choose' - ); - } ); - if ( chooseButton ) { - await chooseButton.asElement()?.click(); - } - } -} diff --git a/packages/js/admin-e2e-tests/src/specs/activate-and-setup/complete-onboarding-wizard.ts b/packages/js/admin-e2e-tests/src/specs/activate-and-setup/complete-onboarding-wizard.ts index 5231ab23351..1ddf0deb86a 100644 --- a/packages/js/admin-e2e-tests/src/specs/activate-and-setup/complete-onboarding-wizard.ts +++ b/packages/js/admin-e2e-tests/src/specs/activate-and-setup/complete-onboarding-wizard.ts @@ -107,11 +107,6 @@ export const testAdminOnboardingWizard = () => { await profileWizard.continue(); } ); - it( 'can complete the theme selection section', async () => { - await profileWizard.themes.isDisplayed(); - await profileWizard.themes.continueWithActiveTheme(); - } ); - it( 'can select the right currency on settings page related to the onboarding country', async () => { const settingsScreen = new WcSettings( page ); await settingsScreen.navigate(); @@ -185,7 +180,7 @@ export const testSelectiveBundleWCPay = () => { await profileWizard.continue(); } ); - it( 'can choose not to install any extensions', async () => { + it( 'can choose not to install any extensions, and finish the rest of the wizard successfully', async () => { await profileWizard.business.freeFeaturesIsDisplayed(); // Add WC Pay check await profileWizard.business.expandRecommendedBusinessFeatures(); @@ -198,13 +193,6 @@ export const testSelectiveBundleWCPay = () => { await profileWizard.continue(); } ); - it( 'can finish the rest of the wizard successfully', async () => { - await profileWizard.themes.isDisplayed(); - - // This navigates to the home screen - await profileWizard.themes.continueWithActiveTheme(); - } ); - it( 'should display the choose payments task, and not the woocommerce payments task', async () => { const homescreen = new WcHomescreen( page ); await homescreen.isDisplayed(); @@ -333,11 +321,8 @@ export const testDifferentStoreCurrenciesWCPay = () => { } await profileWizard.business.uncheckAllRecommendedBusinessFeatures(); - await profileWizard.continue(); - await profileWizard.themes.isDisplayed(); - // This navigates to the home screen - await profileWizard.themes.continueWithActiveTheme(); + await profileWizard.continue(); } ); it( `can select ${ spec.expectedCurrency } as the currency for ${ spec.countryRegion }`, async () => { @@ -583,7 +568,6 @@ export const testBusinessDetailsForm = () => { await profileWizard.business.expandRecommendedBusinessFeatures(); await profileWizard.business.uncheckAllRecommendedBusinessFeatures(); await profileWizard.continue(); - await profileWizard.themes.isDisplayed(); } ); } ); }; diff --git a/packages/js/admin-e2e-tests/src/specs/tasks/purchase.ts b/packages/js/admin-e2e-tests/src/specs/tasks/purchase.ts index 610234e8d45..b2e1c0b5286 100644 --- a/packages/js/admin-e2e-tests/src/specs/tasks/purchase.ts +++ b/packages/js/admin-e2e-tests/src/specs/tasks/purchase.ts @@ -59,40 +59,5 @@ export const testAdminPurchaseSetupTask = () => { ).toBeDefined(); } ); } ); - - describe( 'selecting paid theme', () => { - beforeAll( async () => { - await resetWooCommerceState(); - - await profileWizard.navigate(); - await profileWizard.walkThroughAndCompleteOnboardingWizard( { - themeTitle: 'Blooms', - } ); - - await homeScreen.isDisplayed(); - await homeScreen.possiblyDismissWelcomeModal(); - } ); - - it( 'should display add to my store task', async () => { - expect( - await getElementByText( '*', 'Add Blooms to my store' ) - ).toBeDefined(); - } ); - - it( 'should show paid features modal with option to buy now', async () => { - const task = await getElementByText( - '*', - 'Add Blooms to my store' - ); - await task?.click(); - await waitForElementByText( - 'h1', - 'Would you like to add the following paid features to your store now?' - ); - expect( - await getElementByText( 'button', 'Buy now' ) - ).toBeDefined(); - } ); - } ); } ); }; diff --git a/packages/js/admin-e2e-tests/tsconfig.json b/packages/js/admin-e2e-tests/tsconfig.json index b71c7906e5a..ec5de0e407d 100644 --- a/packages/js/admin-e2e-tests/tsconfig.json +++ b/packages/js/admin-e2e-tests/tsconfig.json @@ -1,8 +1,11 @@ { "extends": "../tsconfig.json", "compilerOptions": { - "composite": true, "rootDir": "src", - "outDir": "build" + "outDir": "build", + "typeRoots": [ + "./typings", + "./node_modules/@types" + ] } } diff --git a/packages/js/admin-layout/.npmrc b/packages/js/admin-layout/.npmrc new file mode 100644 index 00000000000..43c97e719a5 --- /dev/null +++ b/packages/js/admin-layout/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/js/admin-layout/README.md b/packages/js/admin-layout/README.md new file mode 100644 index 00000000000..c5811c8dad5 --- /dev/null +++ b/packages/js/admin-layout/README.md @@ -0,0 +1,11 @@ +# Admin Layout + +A collection of WooCommerce Admin layout components and utilities. + +## Installation + +Install the module + +```bash +pnpm install @woocommerce/product-editor --save +``` diff --git a/packages/js/admin-layout/changelog.md b/packages/js/admin-layout/changelog.md new file mode 100644 index 00000000000..3783eb0da3d --- /dev/null +++ b/packages/js/admin-layout/changelog.md @@ -0,0 +1,3 @@ +# Changelog + +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). diff --git a/packages/js/admin-layout/changelog/add-admin-layout-package b/packages/js/admin-layout/changelog/add-admin-layout-package new file mode 100644 index 00000000000..ab59deae0e8 --- /dev/null +++ b/packages/js/admin-layout/changelog/add-admin-layout-package @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Create @woocommerce/admin-layout package to house header, footer, and similar components and utilities. diff --git a/packages/js/admin-layout/changelog/add-migrate-more-menu-37097 b/packages/js/admin-layout/changelog/add-migrate-more-menu-37097 new file mode 100644 index 00000000000..9b69d3ac60b --- /dev/null +++ b/packages/js/admin-layout/changelog/add-migrate-more-menu-37097 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Adding support for modifying fill name to WooHeaderItem. diff --git a/packages/js/admin-layout/changelog/fix-typescript-incremental-builds b/packages/js/admin-layout/changelog/fix-typescript-incremental-builds new file mode 100644 index 00000000000..f2bdc9a96ae --- /dev/null +++ b/packages/js/admin-layout/changelog/fix-typescript-incremental-builds @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: TypeScript build change + + diff --git a/packages/js/admin-layout/changelog/fix-typescript-package-isolation b/packages/js/admin-layout/changelog/fix-typescript-package-isolation new file mode 100644 index 00000000000..2d087939231 --- /dev/null +++ b/packages/js/admin-layout/changelog/fix-typescript-package-isolation @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Configuration change only + + diff --git a/packages/js/admin-layout/changelog/update-layout-context-for-blocks b/packages/js/admin-layout/changelog/update-layout-context-for-blocks new file mode 100644 index 00000000000..98f1ba467ba --- /dev/null +++ b/packages/js/admin-layout/changelog/update-layout-context-for-blocks @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Adding LayoutContext component and hook. diff --git a/packages/js/admin-layout/changelog/update-webpack-invalid-export-error b/packages/js/admin-layout/changelog/update-webpack-invalid-export-error new file mode 100644 index 00000000000..d844dbd186f --- /dev/null +++ b/packages/js/admin-layout/changelog/update-webpack-invalid-export-error @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update webpack config to use @woocommerce/internal-style-build's parser config diff --git a/packages/js/admin-layout/composer.json b/packages/js/admin-layout/composer.json new file mode 100644 index 00000000000..d843453802e --- /dev/null +++ b/packages/js/admin-layout/composer.json @@ -0,0 +1,32 @@ +{ + "name": "woocommerce/admin-layout", + "description": "WooCommerce Admin layout component library", + "type": "library", + "license": "GPL-3.0-or-later", + "minimum-stability": "dev", + "require-dev": { + "automattic/jetpack-changelogger": "3.3.0" + }, + "config": { + "platform": { + "php": "7.2" + } + }, + "extra": { + "changelogger": { + "formatter": { + "filename": "../../../tools/changelogger/class-package-formatter.php" + }, + "types": { + "fix": "Fixes an existing bug", + "add": "Adds functionality", + "update": "Update existing functionality", + "dev": "Development related task", + "tweak": "A minor adjustment to the codebase", + "performance": "Address performance issues", + "enhancement": "Improve existing functionality" + }, + "changelog": "CHANGELOG.md" + } + } +} diff --git a/packages/js/admin-layout/composer.lock b/packages/js/admin-layout/composer.lock new file mode 100644 index 00000000000..d9d96797c08 --- /dev/null +++ b/packages/js/admin-layout/composer.lock @@ -0,0 +1,483 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "5ce7bfd856ef579554b96ae2f7451072", + "packages": [], + "packages-dev": [ + { + "name": "automattic/jetpack-changelogger", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/Automattic/jetpack-changelogger.git", + "reference": "8f63c829b8d1b0d7b1d5de93510d78523ed18959" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Automattic/jetpack-changelogger/zipball/8f63c829b8d1b0d7b1d5de93510d78523ed18959", + "reference": "8f63c829b8d1b0d7b1d5de93510d78523ed18959", + "shasum": "" + }, + "require": { + "php": ">=5.6", + "symfony/console": "^3.4 || ^5.2 || ^6.0", + "symfony/process": "^3.4 || ^5.2 || ^6.0", + "wikimedia/at-ease": "^1.2 || ^2.0" + }, + "require-dev": { + "wikimedia/testing-access-wrapper": "^1.0 || ^2.0", + "yoast/phpunit-polyfills": "1.0.4" + }, + "bin": [ + "bin/changelogger" + ], + "type": "project", + "extra": { + "autotagger": true, + "branch-alias": { + "dev-trunk": "3.3.x-dev" + }, + "mirror-repo": "Automattic/jetpack-changelogger", + "version-constants": { + "::VERSION": "src/Application.php" + }, + "changelogger": { + "link-template": "https://github.com/Automattic/jetpack-changelogger/compare/${old}...${new}" + } + }, + "autoload": { + "psr-4": { + "Automattic\\Jetpack\\Changelog\\": "lib", + "Automattic\\Jetpack\\Changelogger\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "description": "Jetpack Changelogger tool. Allows for managing changelogs by dropping change files into a changelog directory with each PR.", + "support": { + "source": "https://github.com/Automattic/jetpack-changelogger/tree/v3.3.0" + }, + "time": "2022-12-26T13:49:01+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "symfony/console", + "version": "3.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/a10b1da6fc93080c180bba7219b5ff5b7518fe81", + "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/console/tree/3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "symfony/debug", + "version": "4.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "1a692492190773c5310bc7877cb590c04c2f05be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/1a692492190773c5310bc7877cb590c04c2f05be", + "reference": "1a692492190773c5310bc7877cb590c04c2f05be", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "psr/log": "^1|^2|^3" + }, + "conflict": { + "symfony/http-kernel": "<3.4" + }, + "require-dev": { + "symfony/http-kernel": "^3.4|^4.0|^5.0" + }, + "default-branch": true, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/debug/tree/v4.4.44" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "abandoned": "symfony/error-handler", + "time": "2022-07-28T16:29:46+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "f9c7affe77a00ae32ca127ca6833d034e6d33f25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f9c7affe77a00ae32ca127ca6833d034e6d33f25", + "reference": "f9c7affe77a00ae32ca127ca6833d034e6d33f25", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/main" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-30T17:25:47+00:00" + }, + { + "name": "symfony/process", + "version": "3.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/b8648cf1d5af12a44a51d07ef9bf980921f15fca", + "reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "wikimedia/at-ease", + "version": "v2.0.0", + "source": { + "type": "git", + "url": "https://github.com/wikimedia/at-ease.git", + "reference": "013ac61929797839c80a111a3f1a4710d8248e7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wikimedia/at-ease/zipball/013ac61929797839c80a111a3f1a4710d8248e7a", + "reference": "013ac61929797839c80a111a3f1a4710d8248e7a", + "shasum": "" + }, + "require": { + "php": ">=5.6.99" + }, + "require-dev": { + "jakub-onderka/php-console-highlighter": "0.3.2", + "jakub-onderka/php-parallel-lint": "1.0.0", + "mediawiki/mediawiki-codesniffer": "22.0.0", + "mediawiki/minus-x": "0.3.1", + "ockcyp/covers-validator": "0.5.1 || 0.6.1", + "phpunit/phpunit": "4.8.36 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/Wikimedia/Functions.php" + ], + "psr-4": { + "Wikimedia\\AtEase\\": "src/Wikimedia/AtEase/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Tim Starling", + "email": "tstarling@wikimedia.org" + }, + { + "name": "MediaWiki developers", + "email": "wikitech-l@lists.wikimedia.org" + } + ], + "description": "Safe replacement to @ for suppressing warnings.", + "homepage": "https://www.mediawiki.org/wiki/at-ease", + "support": { + "source": "https://github.com/wikimedia/at-ease/tree/master" + }, + "time": "2018-10-10T15:39:06+00:00" + } + ], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "platform-overrides": { + "php": "7.2" + }, + "plugin-api-version": "2.3.0" +} diff --git a/packages/js/admin-layout/package.json b/packages/js/admin-layout/package.json new file mode 100644 index 00000000000..946ee191af5 --- /dev/null +++ b/packages/js/admin-layout/package.json @@ -0,0 +1,73 @@ +{ + "name": "@woocommerce/admin-layout", + "version": "1.0.0-beta.0", + "description": "WooCommerce admin layout copmonents and utilities.", + "author": "Automattic", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress", + "woocommerce" + ], + "homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/admin-layout/README.md", + "repository": { + "type": "git", + "url": "https://github.com/woocommerce/woocommerce.git" + }, + "bugs": { + "url": "https://github.com/woocommerce/woocommerce/issues" + }, + "main": "build/index.js", + "module": "build-module/index.js", + "types": "build-types", + "react-native": "src/index", + "sideEffects": [ + "build-style/**", + "src/**/*.scss" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "turbo:build": "pnpm run build:js && pnpm run build:css", + "prepare": "composer install", + "changelog": "composer exec -- changelogger", + "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 --project tsconfig.json && tsc --project tsconfig-cjs.json", + "build:css": "webpack", + "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" + }, + "devDependencies": { + "@types/react": "^17.0.2", + "@types/wordpress__components": "^19.10.3", + "@woocommerce/eslint-plugin": "workspace:*", + "@woocommerce/internal-style-build": "workspace:*", + "@wordpress/browserslist-config": "wp-6.0", + "css-loader": "^3.6.0", + "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.9.5", + "webpack": "^5.70.0", + "webpack-cli": "^3.3.12" + }, + "peerDependencies": { + "@types/react": "^17.0.2", + "react": "^17.0.2", + "react-dom": "^17.0.2" + }, + "dependencies": { + "@woocommerce/components": "workspace:*", + "@wordpress/components": "wp-6.0", + "@wordpress/element": "wp-6.0" + } +} diff --git a/packages/js/admin-layout/src/components/LayoutContext/index.ts b/packages/js/admin-layout/src/components/LayoutContext/index.ts new file mode 100644 index 00000000000..a813d5bf269 --- /dev/null +++ b/packages/js/admin-layout/src/components/LayoutContext/index.ts @@ -0,0 +1 @@ +export * from './layout-context'; diff --git a/packages/js/admin-layout/src/components/LayoutContext/layout-context.tsx b/packages/js/admin-layout/src/components/LayoutContext/layout-context.tsx new file mode 100644 index 00000000000..feaedc5be5c --- /dev/null +++ b/packages/js/admin-layout/src/components/LayoutContext/layout-context.tsx @@ -0,0 +1,68 @@ +/** + * External dependencies + */ +import { + createElement, + createContext, + useContext, + useMemo, +} from '@wordpress/element'; + +export type LayoutContextType = { + layoutString: string; + extendLayout: ( item: string ) => LayoutContextType; + layoutParts: string[]; + isDescendantOf: ( item: string ) => boolean; +}; + +type LayoutContextProviderProps = { + children: React.ReactNode; + value: LayoutContextType; +}; + +export const LayoutContext = createContext< LayoutContextType | undefined >( + undefined +); + +export const getLayoutContextValue = ( + layoutParts: LayoutContextType[ 'layoutParts' ] = [] +): LayoutContextType => ( { + layoutParts: [ ...layoutParts ], + extendLayout: ( item ) => { + const newLayoutPath = [ ...layoutParts, item ]; + + return { + ...getLayoutContextValue( newLayoutPath ), + layoutParts: newLayoutPath, + }; + }, + layoutString: layoutParts.join( '/' ), + isDescendantOf: ( item ) => layoutParts.includes( item ), +} ); + +export const LayoutContextProvider: React.FC< LayoutContextProviderProps > = ( { + children, + value, +} ) => ( + + { children } + +); + +export const useLayoutContext = () => { + const layoutContext = useContext( LayoutContext ); + + if ( layoutContext === undefined ) { + throw new Error( + 'useLayoutContext must be used within a LayoutContextProvider' + ); + } + + return layoutContext; +}; + +export const useExtendLayout = ( item: string ) => { + const { extendLayout } = useLayoutContext(); + + return useMemo( () => extendLayout( item ), [ extendLayout, item ] ); +}; diff --git a/packages/js/admin-layout/src/components/index.ts b/packages/js/admin-layout/src/components/index.ts new file mode 100644 index 00000000000..172c9114fdd --- /dev/null +++ b/packages/js/admin-layout/src/components/index.ts @@ -0,0 +1 @@ +export * from './LayoutContext'; diff --git a/packages/js/admin-layout/src/index.ts b/packages/js/admin-layout/src/index.ts new file mode 100644 index 00000000000..229f9f80b3e --- /dev/null +++ b/packages/js/admin-layout/src/index.ts @@ -0,0 +1,2 @@ +export * from './plugins'; +export * from './components'; diff --git a/packages/js/admin-layout/src/plugins/index.ts b/packages/js/admin-layout/src/plugins/index.ts new file mode 100644 index 00000000000..c6cd4096eae --- /dev/null +++ b/packages/js/admin-layout/src/plugins/index.ts @@ -0,0 +1,10 @@ +export { WC_FOOTER_SLOT_NAME, WooFooterItem } from './woo-footer-item'; +export { WC_HEADER_SLOT_NAME, WooHeaderItem } from './woo-header-item'; +export { + WC_HEADER_NAVIGATION_SLOT_NAME, + WooHeaderNavigationItem, +} from './woo-header-navigation-item'; +export { + WC_HEADER_PAGE_TITLE_SLOT_NAME, + WooHeaderPageTitle, +} from './woo-header-page-title'; diff --git a/packages/js/admin-layout/src/plugins/woo-footer-item/index.tsx b/packages/js/admin-layout/src/plugins/woo-footer-item/index.tsx new file mode 100644 index 00000000000..9fe847ea5bf --- /dev/null +++ b/packages/js/admin-layout/src/plugins/woo-footer-item/index.tsx @@ -0,0 +1,51 @@ +/** + * External dependencies + */ +import React from 'react'; +import { Slot, Fill } from '@wordpress/components'; +import { createElement } from '@wordpress/element'; +import { + createOrderedChildren, + sortFillsByOrder, +} from '@woocommerce/components'; + +export const WC_FOOTER_SLOT_NAME = 'woocommerce_footer_item'; + +/** + * Create a Fill for extensions to add items to the WooCommerce Admin footer. + * + * @slotFill WooFooterItem + * @scope woocommerce-admin + * @example + * const MyFooterItem = () => ( + * My header item + * ); + * + * registerPlugin( 'my-extension', { + * render: MyFooterItem, + * scope: 'woocommerce-admin', + * } ); + * @param {Object} param0 + * @param {Array} param0.children - Node children. + * @param {Array} param0.order - Node order. + */ +export const WooFooterItem: React.FC< { + children?: React.ReactNode; + order?: number; +} > & { + Slot: React.FC< Slot.Props >; +} = ( { children, order = 1 } ) => { + return ( + + { ( fillProps: Fill.Props ) => { + return createOrderedChildren( children, order, fillProps ); + } } + + ); +}; + +WooFooterItem.Slot = ( { fillProps } ) => ( + + { sortFillsByOrder } + +); diff --git a/packages/js/admin-layout/src/plugins/woo-header-item/index.tsx b/packages/js/admin-layout/src/plugins/woo-header-item/index.tsx new file mode 100644 index 00000000000..d39cd47d21b --- /dev/null +++ b/packages/js/admin-layout/src/plugins/woo-header-item/index.tsx @@ -0,0 +1,67 @@ +/** + * External dependencies + */ +import React from 'react'; +import { Slot, Fill } from '@wordpress/components'; +import { createElement } from '@wordpress/element'; +import { + createOrderedChildren, + sortFillsByOrder, +} from '@woocommerce/components'; + +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. + * + * @slotFill WooHeaderItem + * @scope woocommerce-admin + * @example + * const MyHeaderItem = () => ( + * My header item + * ); + * + * registerPlugin( 'my-extension', { + * render: MyHeaderItem, + * 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 & { name?: string } >; +} = ( { children, order = 1, name = '' } ) => { + return ( + + { ( fillProps: Fill.Props ) => { + return createOrderedChildren( children, order, fillProps ); + } } + + ); +}; + +WooHeaderItem.Slot = ( { fillProps, name = '' } ) => ( + + { sortFillsByOrder } + +); diff --git a/packages/js/admin-layout/src/plugins/woo-header-navigation-item/index.tsx b/packages/js/admin-layout/src/plugins/woo-header-navigation-item/index.tsx new file mode 100644 index 00000000000..08b4e0b15e4 --- /dev/null +++ b/packages/js/admin-layout/src/plugins/woo-header-navigation-item/index.tsx @@ -0,0 +1,53 @@ +/** + * External dependencies + */ +import React from 'react'; +import { Slot, Fill } from '@wordpress/components'; +import { createElement } from '@wordpress/element'; +import { + createOrderedChildren, + sortFillsByOrder, +} from '@woocommerce/components'; + +export const WC_HEADER_NAVIGATION_SLOT_NAME = + 'woocommerce_header_navigation_item'; + +/** + * Create a Fill for extensions to add items to the WooCommerce Admin + * navigation area left of the page title. + * + * @slotFill WooHeaderNavigationItem + * @scope woocommerce-admin + * @example + * const MyNavigationItem = () => ( + * My nav item + * ); + * + * registerPlugin( 'my-extension', { + * render: MyNavigationItem, + * scope: 'woocommerce-admin', + * } ); + * @param {Object} param0 + * @param {Array} param0.children - Node children. + * @param {Array} param0.order - Node order. + */ +export const WooHeaderNavigationItem: React.FC< { + children?: React.ReactNode; + order?: number; +} > & { + Slot: React.FC< Slot.Props >; +} = ( { children, order = 1 } ) => { + return ( + + { ( fillProps: Fill.Props ) => { + return createOrderedChildren( children, order, fillProps ); + } } + + ); +}; + +WooHeaderNavigationItem.Slot = ( { fillProps }: Slot.Props ) => ( + + { sortFillsByOrder } + +); diff --git a/packages/js/admin-layout/src/plugins/woo-header-page-title/index.tsx b/packages/js/admin-layout/src/plugins/woo-header-page-title/index.tsx new file mode 100644 index 00000000000..8e0b8b307e5 --- /dev/null +++ b/packages/js/admin-layout/src/plugins/woo-header-page-title/index.tsx @@ -0,0 +1,41 @@ +/** + * External dependencies + */ +import React from 'react'; +import { Slot, Fill } from '@wordpress/components'; +import { createElement, Fragment } from '@wordpress/element'; + +export const WC_HEADER_PAGE_TITLE_SLOT_NAME = 'woocommerce_header_page_title'; + +/** + * Create a Fill for extensions to add custom page titles. + * + * @slotFill WooHeaderPageTitle + * @scope woocommerce-admin + * @example + * const MyPageTitle = () => ( + * My page title + * ); + * + * registerPlugin( 'my-page-title', { + * render: MyPageTitle, + * scope: 'woocommerce-admin', + * } ); + * @param {Object} param0 + * @param {Array} param0.children - Node children. + */ +export const WooHeaderPageTitle: React.FC< { + children?: React.ReactNode; +} > & { + Slot: React.FC< Slot.Props >; +} = ( { children } ) => { + return { children }; +}; + +WooHeaderPageTitle.Slot = ( { fillProps } ) => ( + + { ( fills ) => { + return <>{ [ ...fills ].pop() }; + } } + +); diff --git a/packages/js/admin-layout/src/style.scss b/packages/js/admin-layout/src/style.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/js/admin-layout/tsconfig-cjs.json b/packages/js/admin-layout/tsconfig-cjs.json new file mode 100644 index 00000000000..c8b5b6a6b96 --- /dev/null +++ b/packages/js/admin-layout/tsconfig-cjs.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig-cjs", + "compilerOptions": { + "outDir": "build", + "typeRoots": [ + "./typings", + "./node_modules/@types" + ] + } +} diff --git a/packages/js/admin-layout/tsconfig.json b/packages/js/admin-layout/tsconfig.json new file mode 100644 index 00000000000..c5f351a60cc --- /dev/null +++ b/packages/js/admin-layout/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig", + "compilerOptions": { + "rootDir": "src", + "outDir": "build-module", + "declaration": true, + "declarationMap": true, + "declarationDir": "./build-types", + "typeRoots": [ + "./typings", + "./node_modules/@types" + ] + } +} diff --git a/packages/js/admin-layout/webpack.config.js b/packages/js/admin-layout/webpack.config.js new file mode 100644 index 00000000000..9b42746f1dd --- /dev/null +++ b/packages/js/admin-layout/webpack.config.js @@ -0,0 +1,19 @@ +/** + * Internal dependencies + */ +const { webpackConfig } = require( '@woocommerce/internal-style-build' ); + +module.exports = { + mode: process.env.NODE_ENV || 'development', + entry: { + 'build-style': __dirname + '/src/style.scss', + }, + output: { + path: __dirname, + }, + module: { + parser: webpackConfig.parser, + rules: webpackConfig.rules, + }, + plugins: webpackConfig.plugins, +}; diff --git a/packages/js/api-core-tests/package.json b/packages/js/api-core-tests/package.json index d176de4d7b6..637d15cccd3 100644 --- a/packages/js/api-core-tests/package.json +++ b/packages/js/api-core-tests/package.json @@ -4,8 +4,8 @@ "description": "API tests for WooCommerce", "main": "index.js", "engines": { - "node": "^16.13.1", - "pnpm": "^7.13.3" + "node": "^16.14.1", + "pnpm": "^8.3.1" }, "scripts": { "e2e": "jest", @@ -36,7 +36,7 @@ }, "devDependencies": { "@woocommerce/eslint-plugin": "workspace:*", - "eslint": "^8.12.0" + "eslint": "^8.32.0" }, "publishConfig": { "access": "public" diff --git a/packages/js/api/changelog/dev-adjust-sync b/packages/js/api/changelog/dev-adjust-sync new file mode 100644 index 00000000000..f11d1e352f4 --- /dev/null +++ b/packages/js/api/changelog/dev-adjust-sync @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Dev dependency bump + + diff --git a/packages/js/api/changelog/dev-consolidate-eslint-versions b/packages/js/api/changelog/dev-consolidate-eslint-versions new file mode 100644 index 00000000000..d3d95c39119 --- /dev/null +++ b/packages/js/api/changelog/dev-consolidate-eslint-versions @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update eslint to 8.32.0 across the monorepo. diff --git a/packages/js/api/changelog/dev-pin-wp-deps-6 b/packages/js/api/changelog/dev-pin-wp-deps-6 new file mode 100644 index 00000000000..955f4a22095 --- /dev/null +++ b/packages/js/api/changelog/dev-pin-wp-deps-6 @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Syncpack update of @typescript/eslint dependencies. diff --git a/packages/js/api/changelog/dev-update-pnpm8 b/packages/js/api/changelog/dev-update-pnpm8 new file mode 100644 index 00000000000..c21e013f454 --- /dev/null +++ b/packages/js/api/changelog/dev-update-pnpm8 @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Update pnpm to version 8. diff --git a/packages/js/api/changelog/fix-typescript-incremental-builds b/packages/js/api/changelog/fix-typescript-incremental-builds new file mode 100644 index 00000000000..f2bdc9a96ae --- /dev/null +++ b/packages/js/api/changelog/fix-typescript-incremental-builds @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: TypeScript build change + + diff --git a/packages/js/api/changelog/fix-typescript-package-isolation b/packages/js/api/changelog/fix-typescript-package-isolation new file mode 100644 index 00000000000..2d087939231 --- /dev/null +++ b/packages/js/api/changelog/fix-typescript-package-isolation @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Configuration change only + + diff --git a/packages/js/api/changelog/update-changelogger b/packages/js/api/changelog/update-changelogger new file mode 100644 index 00000000000..1674c919e78 --- /dev/null +++ b/packages/js/api/changelog/update-changelogger @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: Dev dependency update. + + diff --git a/packages/js/api/composer.json b/packages/js/api/composer.json index 384ed03b22e..02a86b11c3a 100644 --- a/packages/js/api/composer.json +++ b/packages/js/api/composer.json @@ -5,7 +5,7 @@ "license": "GPL-3.0-or-later", "minimum-stability": "dev", "require-dev": { - "automattic/jetpack-changelogger": "3.1.3" + "automattic/jetpack-changelogger": "3.3.0" }, "config": { "platform": { diff --git a/packages/js/api/composer.lock b/packages/js/api/composer.lock index e2ee0a36abe..aa3ed289cf2 100644 --- a/packages/js/api/composer.lock +++ b/packages/js/api/composer.lock @@ -4,32 +4,32 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2ac4a9ea3ab4687cb26b0075128628de", + "content-hash": "8cdc2ba8c2e8669b3d48cf7e4cb3c379", "packages": [], "packages-dev": [ { "name": "automattic/jetpack-changelogger", - "version": "v3.1.3", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/Automattic/jetpack-changelogger.git", - "reference": "cdd256d8ba6369f82d9377de7e9e2598e3e16ae0" + "reference": "8f63c829b8d1b0d7b1d5de93510d78523ed18959" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Automattic/jetpack-changelogger/zipball/cdd256d8ba6369f82d9377de7e9e2598e3e16ae0", - "reference": "cdd256d8ba6369f82d9377de7e9e2598e3e16ae0", + "url": "https://api.github.com/repos/Automattic/jetpack-changelogger/zipball/8f63c829b8d1b0d7b1d5de93510d78523ed18959", + "reference": "8f63c829b8d1b0d7b1d5de93510d78523ed18959", "shasum": "" }, "require": { "php": ">=5.6", - "symfony/console": "^3.4 || ^5.2", - "symfony/process": "^3.4 || ^5.2", + "symfony/console": "^3.4 || ^5.2 || ^6.0", + "symfony/process": "^3.4 || ^5.2 || ^6.0", "wikimedia/at-ease": "^1.2 || ^2.0" }, "require-dev": { "wikimedia/testing-access-wrapper": "^1.0 || ^2.0", - "yoast/phpunit-polyfills": "1.0.3" + "yoast/phpunit-polyfills": "1.0.4" }, "bin": [ "bin/changelogger" @@ -38,7 +38,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "3.1.x-dev" + "dev-trunk": "3.3.x-dev" }, "mirror-repo": "Automattic/jetpack-changelogger", "version-constants": { @@ -60,9 +60,9 @@ ], "description": "Jetpack Changelogger tool. Allows for managing changelogs by dropping change files into a changelog directory with each PR.", "support": { - "source": "https://github.com/Automattic/jetpack-changelogger/tree/v3.1.3" + "source": "https://github.com/Automattic/jetpack-changelogger/tree/v3.3.0" }, - "time": "2022-06-21T07:31:56+00:00" + "time": "2022-12-26T13:49:01+00:00" }, { "name": "psr/log", @@ -204,12 +204,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "6637e62480b60817b9a6984154a533e8e64c6bd5" + "reference": "1a692492190773c5310bc7877cb590c04c2f05be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/6637e62480b60817b9a6984154a533e8e64c6bd5", - "reference": "6637e62480b60817b9a6984154a533e8e64c6bd5", + "url": "https://api.github.com/repos/symfony/debug/zipball/1a692492190773c5310bc7877cb590c04c2f05be", + "reference": "1a692492190773c5310bc7877cb590c04c2f05be", "shasum": "" }, "require": { @@ -249,7 +249,7 @@ "description": "Provides tools to ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/debug/tree/v4.4.41" + "source": "https://github.com/symfony/debug/tree/v4.4.44" }, "funding": [ { @@ -266,7 +266,7 @@ } ], "abandoned": "symfony/error-handler", - "time": "2022-04-12T15:19:55+00:00" + "time": "2022-07-28T16:29:46+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -274,12 +274,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { @@ -295,7 +295,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -334,7 +334,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -350,7 +350,7 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/process", diff --git a/packages/js/api/package.json b/packages/js/api/package.json index 588449eeb8d..369e22c83fe 100644 --- a/packages/js/api/package.json +++ b/packages/js/api/package.json @@ -5,8 +5,8 @@ "description": "A simple interface for interacting with a WooCommerce installation.", "homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/api/README.md", "engines": { - "node": "^16.13.1", - "pnpm": "^7.13.3" + "node": "^16.14.1", + "pnpm": "^8.3.1" }, "repository": { "type": "git", @@ -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,15 +50,15 @@ "devDependencies": { "@types/create-hmac": "1.1.0", "@types/jest": "^27.4.1", - "@types/node": "13.13.5", - "@typescript-eslint/eslint-plugin": "^5.3.1", - "@typescript-eslint/parser": "^5.3.1", + "@types/node": "^16.18.18", + "@typescript-eslint/eslint-plugin": "^5.54.0", + "@typescript-eslint/parser": "^5.54.0", "@woocommerce/eslint-plugin": "workspace:*", "axios-mock-adapter": "^1.20.0", - "eslint": "^8.2.0", + "eslint": "^8.32.0", "jest": "^27", "ts-jest": "^27", - "typescript": "^4.8.3" + "typescript": "^4.9.5" }, "publishConfig": { "access": "public" diff --git a/packages/js/api/tsconfig.json b/packages/js/api/tsconfig.json index 8ef7d238961..0379fdd24a9 100644 --- a/packages/js/api/tsconfig.json +++ b/packages/js/api/tsconfig.json @@ -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/" ] } diff --git a/packages/js/components/.eslintrc.js b/packages/js/components/.eslintrc.js index f740ae8d831..4c86962405c 100644 --- a/packages/js/components/.eslintrc.js +++ b/packages/js/components/.eslintrc.js @@ -1,6 +1,7 @@ module.exports = { extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ], root: true, + ignorePatterns: [ '**/test/*.ts', '**/test/*.tsx' ], overrides: [ { files: [ diff --git a/packages/js/components/CHANGELOG.md b/packages/js/components/CHANGELOG.md index a8bb8684c2a..a7a86402539 100644 --- a/packages/js/components/CHANGELOG.md +++ b/packages/js/components/CHANGELOG.md @@ -2,6 +2,41 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [12.0.0](https://www.npmjs.com/package/@woocommerce/components/v/12.0.0) - 2022-12-28 + +- Patch - Add name to exported popover slot used to display SelectControl Menu, so it is only used for SelectControl menus. [#36124] +- Patch - Close DateTimePickerControl's dropdown when blurring from input. [#36124] +- Patch - DateTimePickerControl's onChange now only fires when there is an actual change to the datetime. [#36124] +- Patch - Fix DateTimePickerControl's popover styling when slot-fill is used. [#36124] +- Patch - Fixed DatePicker to work in WordPress 6.1 when currentDate is set to a moment instance. [#36124] +- Patch - Fix pagination label text from uppercase to normal and font styles [#36124] +- Patch - Include react-dates styles (no longer in WP 6.1+). [#36124] +- Minor - Set editor mode on initialization to prevent initial text editor focus [#36124] +- Patch - Set initial values prop from reset form function as optional [#36124] +- Patch - Add aria-label for simple select dropdown [#36124] +- Patch - Add async filtering support to the `__experimentalSelectControl` component [#36124] +- Minor - Add className prop to ListItem. [#36124] +- Minor - Add className prop to Sortable [#36124] +- Minor - Added ability to force time when DateTimePickerControl is date-only (timeForDateOnly prop). [#36124] +- Minor - Add experimental ConditionalWrapper component [#36124] +- Patch - Add experimental open menu when user focus the select control input element [#36124] +- Minor - Adding isHidden option for primary button in TourKit component. [#36124] +- Minor - Add support for custom suffix prop on SelectControl. [#36124] +- Minor - Make Table component accept className prop. [#36124] +- Minor - Move classname down in SelectControl Menu so it is on the actual Menu element. [#36124] +- Major [ **BREAKING CHANGE** ] - Switch DateTimePickerControl formatting to PHP style, for WP compatibility. [#36124] +- Patch - Updating downshift to 6.1.12. [#36124] +- Minor - Allow the user to select multiple images in the Media Library [#36124] +- Patch - Migrate search component to TS [#36124] +- Minor - Move file picker by clicking card into the MediaUploader component [#36124] +- Minor - Fix up initial block selection in RichTextEditor and add media blocks [#36124] +- Patch - Updated image gallery toolbar position and tooltips. [#36124] +- Patch - Update variable name within useFormContext. [#36124] +- Minor - Add noDataLabel property into table.js component to allow No Data label customization. [#36124] +- Patch - Align the field height across the whole form [#36124] +- Patch - Fade the value selection field in the Attributes modal when no attribute is added [#36124] +- Patch - Update font size and spacing in the tooltip component [#36124] + ## [11.1.0](https://www.npmjs.com/package/@woocommerce/components/v/11.1.0) - 2022-10-24 - Minor - Allow passing of additional props to form inputs [#35160] diff --git a/packages/js/components/changelog/add-35046 b/packages/js/components/changelog/add-35046 deleted file mode 100644 index e9d7806593d..00000000000 --- a/packages/js/components/changelog/add-35046 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Add experimental ConditionalWrapper component diff --git a/packages/js/components/changelog/add-35851-tree-control b/packages/js/components/changelog/add-35851-tree-control new file mode 100644 index 00000000000..24395c6622b --- /dev/null +++ b/packages/js/components/changelog/add-35851-tree-control @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Create tree-control component diff --git a/packages/js/components/changelog/add-35851-tree-control-a11y b/packages/js/components/changelog/add-35851-tree-control-a11y new file mode 100644 index 00000000000..fe500d76cd1 --- /dev/null +++ b/packages/js/components/changelog/add-35851-tree-control-a11y @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Add a11y support for the Tree component diff --git a/packages/js/components/changelog/add-35851-tree-control-custom-label b/packages/js/components/changelog/add-35851-tree-control-custom-label new file mode 100644 index 00000000000..74d9e8185de --- /dev/null +++ b/packages/js/components/changelog/add-35851-tree-control-custom-label @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Add custom rendering logic to the item label diff --git a/packages/js/components/changelog/add-35851-tree-control-expander b/packages/js/components/changelog/add-35851-tree-control-expander new file mode 100644 index 00000000000..943ea1225d5 --- /dev/null +++ b/packages/js/components/changelog/add-35851-tree-control-expander @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Add TreeControl expand/collapse functionality. diff --git a/packages/js/components/changelog/add-35851-tree-control-highlight-item b/packages/js/components/changelog/add-35851-tree-control-highlight-item new file mode 100644 index 00000000000..b4bef1f926e --- /dev/null +++ b/packages/js/components/changelog/add-35851-tree-control-highlight-item @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Add highlighter to the tree control diff --git a/packages/js/components/changelog/add-35851-tree-control-selection b/packages/js/components/changelog/add-35851-tree-control-selection new file mode 100644 index 00000000000..e2cedaf2afd --- /dev/null +++ b/packages/js/components/changelog/add-35851-tree-control-selection @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Add selection logic to tree control component diff --git a/packages/js/components/changelog/add-36014-mvp-field-slot b/packages/js/components/changelog/add-36014-mvp-field-slot new file mode 100644 index 00000000000..d4ba7a6730e --- /dev/null +++ b/packages/js/components/changelog/add-36014-mvp-field-slot @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Adding WooProductFieldItem slotfill. diff --git a/packages/js/components/changelog/add-36015-mvp-section-slot b/packages/js/components/changelog/add-36015-mvp-section-slot new file mode 100644 index 00000000000..e22146e0c31 --- /dev/null +++ b/packages/js/components/changelog/add-36015-mvp-section-slot @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Adding the WooProductSectionItem slotfill component. diff --git a/packages/js/components/changelog/add-36074_basic_fields b/packages/js/components/changelog/add-36074_basic_fields new file mode 100644 index 00000000000..cc93a0ca6c4 --- /dev/null +++ b/packages/js/components/changelog/add-36074_basic_fields @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add 6 basic fields to the product fields registry for use in extensibility within the new Product MVP. diff --git a/packages/js/components/changelog/add-36074_product_form_field_registry b/packages/js/components/changelog/add-36074_product_form_field_registry new file mode 100644 index 00000000000..bef870437e3 --- /dev/null +++ b/packages/js/components/changelog/add-36074_product_form_field_registry @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add product field store and helper functions for rendering fields from config. diff --git a/packages/js/components/changelog/add-36248-added-flexibility-to-plugins b/packages/js/components/changelog/add-36248-added-flexibility-to-plugins new file mode 100644 index 00000000000..822bad3a2cb --- /dev/null +++ b/packages/js/components/changelog/add-36248-added-flexibility-to-plugins @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Added LearnMore option as well as made it possible to use this button multiple instances on the page diff --git a/packages/js/components/changelog/add-36355_new_product_editor_package b/packages/js/components/changelog/add-36355_new_product_editor_package new file mode 100644 index 00000000000..19797698691 --- /dev/null +++ b/packages/js/components/changelog/add-36355_new_product_editor_package @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Move experimental product section components to @woocommerce/product-editor package. diff --git a/packages/js/components/changelog/add-36419-mvp-pricing-slot-fill b/packages/js/components/changelog/add-36419-mvp-pricing-slot-fill new file mode 100644 index 00000000000..015aaf6ddab --- /dev/null +++ b/packages/js/components/changelog/add-36419-mvp-pricing-slot-fill @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Altering styles to correctly target fields within slot fills on product editor. diff --git a/packages/js/components/changelog/add-36991 b/packages/js/components/changelog/add-36991 new file mode 100644 index 00000000000..8a6bde487ff --- /dev/null +++ b/packages/js/components/changelog/add-36991 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Fix dependency versions diff --git a/packages/js/components/changelog/add-37263_category_field_block b/packages/js/components/changelog/add-37263_category_field_block new file mode 100644 index 00000000000..49965698893 --- /dev/null +++ b/packages/js/components/changelog/add-37263_category_field_block @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Fix issue where width of select control dropdown was not correctly calculated when rendering was delayed. diff --git a/packages/js/components/changelog/add-37330 b/packages/js/components/changelog/add-37330 new file mode 100644 index 00000000000..a7796979a59 --- /dev/null +++ b/packages/js/components/changelog/add-37330 @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Improve a11y support to collapsible content component diff --git a/packages/js/components/changelog/add-input-props-to-exp-select b/packages/js/components/changelog/add-input-props-to-exp-select new file mode 100644 index 00000000000..a4123d1f6ea --- /dev/null +++ b/packages/js/components/changelog/add-input-props-to-exp-select @@ -0,0 +1,4 @@ +Significance: patch +Type: add + +Add an optional "InputProps" to experimental SelectControl component diff --git a/packages/js/components/changelog/add-toggle-content-block-37253 b/packages/js/components/changelog/add-toggle-content-block-37253 new file mode 100644 index 00000000000..38395751212 --- /dev/null +++ b/packages/js/components/changelog/add-toggle-content-block-37253 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Adding simple DisplayState wrapper and modifying Collapsible component to allow rendering hidden content. diff --git a/packages/js/components/changelog/add-tree-select b/packages/js/components/changelog/add-tree-select new file mode 100644 index 00000000000..6bf7d04c3de --- /dev/null +++ b/packages/js/components/changelog/add-tree-select @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Create SelectTree component that uses TreeControl diff --git a/packages/js/components/changelog/dev-consolidate-eslint-versions b/packages/js/components/changelog/dev-consolidate-eslint-versions new file mode 100644 index 00000000000..d3d95c39119 --- /dev/null +++ b/packages/js/components/changelog/dev-consolidate-eslint-versions @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update eslint to 8.32.0 across the monorepo. diff --git a/packages/js/components/changelog/dev-migrate-ellipsis-menu-to-ts b/packages/js/components/changelog/dev-migrate-ellipsis-menu-to-ts new file mode 100644 index 00000000000..81da1ab3e52 --- /dev/null +++ b/packages/js/components/changelog/dev-migrate-ellipsis-menu-to-ts @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Migrate ellipsis-menu component to TS \ No newline at end of file diff --git a/packages/js/components/changelog/dev-migrate-link-ts b/packages/js/components/changelog/dev-migrate-link-ts new file mode 100644 index 00000000000..774c442e98f --- /dev/null +++ b/packages/js/components/changelog/dev-migrate-link-ts @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Migrate Link component to TS \ No newline at end of file diff --git a/packages/js/components/changelog/dev-migrate-product-image-component-to-ts b/packages/js/components/changelog/dev-migrate-product-image-component-to-ts new file mode 100644 index 00000000000..c86b6f4ce92 --- /dev/null +++ b/packages/js/components/changelog/dev-migrate-product-image-component-to-ts @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Migrate ProductImage component to TS \ No newline at end of file diff --git a/packages/js/components/changelog/dev-migrate-rating-component-to-ts b/packages/js/components/changelog/dev-migrate-rating-component-to-ts new file mode 100644 index 00000000000..ee181ed57f8 --- /dev/null +++ b/packages/js/components/changelog/dev-migrate-rating-component-to-ts @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Migrate Rating component to TS \ No newline at end of file diff --git a/packages/js/components/changelog/dev-migrate-section-component-to-ts b/packages/js/components/changelog/dev-migrate-section-component-to-ts new file mode 100644 index 00000000000..66400fa1063 --- /dev/null +++ b/packages/js/components/changelog/dev-migrate-section-component-to-ts @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Migrate Section component to TS \ No newline at end of file diff --git a/packages/js/components/changelog/dev-migrate-table-component-to-ts b/packages/js/components/changelog/dev-migrate-table-component-to-ts new file mode 100644 index 00000000000..0f3389fca67 --- /dev/null +++ b/packages/js/components/changelog/dev-migrate-table-component-to-ts @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Migrate Table component to TS \ No newline at end of file diff --git a/packages/js/components/changelog/dev-migrate-tag-component-to-ts b/packages/js/components/changelog/dev-migrate-tag-component-to-ts new file mode 100644 index 00000000000..e8c0ed291c6 --- /dev/null +++ b/packages/js/components/changelog/dev-migrate-tag-component-to-ts @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Migrate Tag component to TS diff --git a/packages/js/components/changelog/dev-pin-wp-deps-6 b/packages/js/components/changelog/dev-pin-wp-deps-6 new file mode 100644 index 00000000000..551e0919dac --- /dev/null +++ b/packages/js/components/changelog/dev-pin-wp-deps-6 @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Sync @wordpress package versions via syncpack. diff --git a/packages/js/components/changelog/dev-update-eslint-plugin b/packages/js/components/changelog/dev-update-eslint-plugin new file mode 100644 index 00000000000..fdc4c9f00cd --- /dev/null +++ b/packages/js/components/changelog/dev-update-eslint-plugin @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Adjust eslintrc for changes to eslint plugin. diff --git a/packages/js/components/changelog/dev-update-pnpm8 b/packages/js/components/changelog/dev-update-pnpm8 new file mode 100644 index 00000000000..c21e013f454 --- /dev/null +++ b/packages/js/components/changelog/dev-update-pnpm8 @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Update pnpm to version 8. diff --git a/packages/js/components/changelog/enhancement-35190-update-tooltip-styles b/packages/js/components/changelog/enhancement-35190-update-tooltip-styles deleted file mode 100644 index 4d21861d602..00000000000 --- a/packages/js/components/changelog/enhancement-35190-update-tooltip-styles +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: enhancement - -Update font size and spacing in the tooltip component diff --git a/packages/js/components/changelog/fix-36256_moving_list_item_moves_image b/packages/js/components/changelog/fix-36256_moving_list_item_moves_image new file mode 100644 index 00000000000..2584c971946 --- /dev/null +++ b/packages/js/components/changelog/fix-36256_moving_list_item_moves_image @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Fix SortableItem duplicated id diff --git a/packages/js/components/changelog/fix-36614_refactor_create_ordered_children b/packages/js/components/changelog/fix-36614_refactor_create_ordered_children new file mode 100644 index 00000000000..a347e3adc16 --- /dev/null +++ b/packages/js/components/changelog/fix-36614_refactor_create_ordered_children @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Refactor createOrderedChildren diff --git a/packages/js/components/changelog/fix-37348 b/packages/js/components/changelog/fix-37348 new file mode 100644 index 00000000000..ae0096b28fc --- /dev/null +++ b/packages/js/components/changelog/fix-37348 @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Prevent duplicate registration of core blocks in client diff --git a/packages/js/components/changelog/fix-create_ordered_children b/packages/js/components/changelog/fix-create_ordered_children new file mode 100644 index 00000000000..18b502f176d --- /dev/null +++ b/packages/js/components/changelog/fix-create_ordered_children @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix issue were Options tab was not showing up anymore in new product management screen. diff --git a/packages/js/components/changelog/fix-date-time-picker-control-suffix-style b/packages/js/components/changelog/fix-date-time-picker-control-suffix-style deleted file mode 100644 index 97b797ed6cb..00000000000 --- a/packages/js/components/changelog/fix-date-time-picker-control-suffix-style +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fix -Comment: Just a minor tweak to the CSS for the DateTimePickerControl suffix. - - diff --git a/packages/js/components/changelog/fix-experimental-tree-control-styles b/packages/js/components/changelog/fix-experimental-tree-control-styles new file mode 100644 index 00000000000..b1795694d0c --- /dev/null +++ b/packages/js/components/changelog/fix-experimental-tree-control-styles @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Include CSS for experimental tree control so it renders properly in Storybook. diff --git a/packages/js/components/changelog/fix-select-control-popover-slots b/packages/js/components/changelog/fix-select-control-popover-slots deleted file mode 100644 index 81d59f026ab..00000000000 --- a/packages/js/components/changelog/fix-select-control-popover-slots +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Add name to exported popover slot used to display SelectControl Menu, so it is only used for SelectControl menus. diff --git a/packages/js/components/changelog/fix-select-control-style b/packages/js/components/changelog/fix-select-control-style new file mode 100644 index 00000000000..256abe194ed --- /dev/null +++ b/packages/js/components/changelog/fix-select-control-style @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix SelectControl and TreeControl styles. diff --git a/packages/js/components/changelog/fix-setstate-diff-component b/packages/js/components/changelog/fix-setstate-diff-component new file mode 100644 index 00000000000..85d405d9d99 --- /dev/null +++ b/packages/js/components/changelog/fix-setstate-diff-component @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: Move registerFill call to inside an useEffect since it was updating a component while rendering another component + + diff --git a/packages/js/components/changelog/fix-tour-kit-documentation b/packages/js/components/changelog/fix-tour-kit-documentation new file mode 100644 index 00000000000..d2df9ede44b --- /dev/null +++ b/packages/js/components/changelog/fix-tour-kit-documentation @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update TourKit README to correct primaryButton example and formatting. diff --git a/packages/js/components/changelog/fix-typescript-incremental-builds b/packages/js/components/changelog/fix-typescript-incremental-builds new file mode 100644 index 00000000000..f2bdc9a96ae --- /dev/null +++ b/packages/js/components/changelog/fix-typescript-incremental-builds @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: TypeScript build change + + diff --git a/packages/js/components/changelog/fix-typescript-incremental-regression b/packages/js/components/changelog/fix-typescript-incremental-regression new file mode 100644 index 00000000000..2d087939231 --- /dev/null +++ b/packages/js/components/changelog/fix-typescript-incremental-regression @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Configuration change only + + diff --git a/packages/js/components/changelog/fix-typescript-package-isolation b/packages/js/components/changelog/fix-typescript-package-isolation new file mode 100644 index 00000000000..2d087939231 --- /dev/null +++ b/packages/js/components/changelog/fix-typescript-package-isolation @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Configuration change only + + diff --git a/packages/js/components/changelog/refactor-36446_product_tabs b/packages/js/components/changelog/refactor-36446_product_tabs new file mode 100644 index 00000000000..9a911a106b4 --- /dev/null +++ b/packages/js/components/changelog/refactor-36446_product_tabs @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add new WooProductTabItem component for slot filling tab items. diff --git a/packages/js/components/changelog/try-product-mvp-slotfill-experiments b/packages/js/components/changelog/try-product-mvp-slotfill-experiments new file mode 100644 index 00000000000..7df70dd246d --- /dev/null +++ b/packages/js/components/changelog/try-product-mvp-slotfill-experiments @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Adding experimental component SlotContext diff --git a/packages/js/components/changelog/try-support-multiple-fills b/packages/js/components/changelog/try-support-multiple-fills new file mode 100644 index 00000000000..901059348bc --- /dev/null +++ b/packages/js/components/changelog/try-support-multiple-fills @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Updating the product editor fill components to support multiple targets. diff --git a/packages/js/components/changelog/update-34885_category_field_in_product_editor b/packages/js/components/changelog/update-34885_category_field_in_product_editor new file mode 100644 index 00000000000..a9d41164d98 --- /dev/null +++ b/packages/js/components/changelog/update-34885_category_field_in_product_editor @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Export TreeSelectControl component and add additional props: onInputChange, alwaysShowPlaceholder, includeParent. diff --git a/packages/js/components/changelog/update-34885_category_field_in_product_editor_select_control b/packages/js/components/changelog/update-34885_category_field_in_product_editor_select_control new file mode 100644 index 00000000000..f4635d9f8c6 --- /dev/null +++ b/packages/js/components/changelog/update-34885_category_field_in_product_editor_select_control @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Fix issue where single item can not be cleared and text can not be selected upon click. diff --git a/packages/js/components/changelog/update-34885_category_field_in_product_editor_tree_changes b/packages/js/components/changelog/update-34885_category_field_in_product_editor_tree_changes new file mode 100644 index 00000000000..95ce9ecb8af --- /dev/null +++ b/packages/js/components/changelog/update-34885_category_field_in_product_editor_tree_changes @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add minFilterQueryLength, individuallySelectParent, and clearOnSelect props. diff --git a/packages/js/components/changelog/update-36016-product-details-slotfill b/packages/js/components/changelog/update-36016-product-details-slotfill new file mode 100644 index 00000000000..d03cd2109ac --- /dev/null +++ b/packages/js/components/changelog/update-36016-product-details-slotfill @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Adding ProductSectionLayout component and changing default order for WooProductSectionItem component. diff --git a/packages/js/components/changelog/update-36355_product_editor_package b/packages/js/components/changelog/update-36355_product_editor_package new file mode 100644 index 00000000000..d00b4d6684e --- /dev/null +++ b/packages/js/components/changelog/update-36355_product_editor_package @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Add deprecated message to product slot fill components diff --git a/packages/js/components/changelog/update-36610-support-variations-alternate b/packages/js/components/changelog/update-36610-support-variations-alternate new file mode 100644 index 00000000000..124a49aaa9f --- /dev/null +++ b/packages/js/components/changelog/update-36610-support-variations-alternate @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Updating WooProductFieldItem to uniquely generate IDs with different sections. diff --git a/packages/js/components/changelog/update-37955_disable_drag_and_drop_images_block b/packages/js/components/changelog/update-37955_disable_drag_and_drop_images_block new file mode 100644 index 00000000000..436e516160c --- /dev/null +++ b/packages/js/components/changelog/update-37955_disable_drag_and_drop_images_block @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add allowDragging option to ImageGallery to support disabling drag and drop of images. diff --git a/packages/js/components/changelog/update-changelogger b/packages/js/components/changelog/update-changelogger new file mode 100644 index 00000000000..1674c919e78 --- /dev/null +++ b/packages/js/components/changelog/update-changelogger @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: Dev dependency update. + + diff --git a/packages/js/components/changelog/update-date-time-picker-control-force-time-to b/packages/js/components/changelog/update-date-time-picker-control-force-time-to deleted file mode 100644 index fcf4278fc5f..00000000000 --- a/packages/js/components/changelog/update-date-time-picker-control-force-time-to +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Added ability to force time when DateTimePickerControl is date-only (timeForDateOnly prop). diff --git a/packages/js/components/changelog/update-date-time-picker-control-formatting b/packages/js/components/changelog/update-date-time-picker-control-formatting deleted file mode 100644 index 800e0dbc01d..00000000000 --- a/packages/js/components/changelog/update-date-time-picker-control-formatting +++ /dev/null @@ -1,4 +0,0 @@ -Significance: major -Type: update - -Switch DateTimePickerControl formatting to PHP style, for WP compatibility. diff --git a/packages/js/components/changelog/update-experimental-select-control b/packages/js/components/changelog/update-experimental-select-control new file mode 100644 index 00000000000..8b315f59b12 --- /dev/null +++ b/packages/js/components/changelog/update-experimental-select-control @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Apply wccom experimental select control changes diff --git a/packages/js/components/changelog/update-interpolate-advanced-filters b/packages/js/components/changelog/update-interpolate-advanced-filters new file mode 100644 index 00000000000..f7a321ec0e7 --- /dev/null +++ b/packages/js/components/changelog/update-interpolate-advanced-filters @@ -0,0 +1,4 @@ +Significance: major +Type: update + +Updated AdvancedFilter to use createInterpolateElement instead of interpolateComponents. diff --git a/packages/js/components/changelog/update-migrate-category-field b/packages/js/components/changelog/update-migrate-category-field new file mode 100644 index 00000000000..6c8e15c88a6 --- /dev/null +++ b/packages/js/components/changelog/update-migrate-category-field @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Add unit tests + + diff --git a/packages/js/components/changelog/update-product_editor_packages b/packages/js/components/changelog/update-product_editor_packages new file mode 100644 index 00000000000..5e7d389add7 --- /dev/null +++ b/packages/js/components/changelog/update-product_editor_packages @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Add deprecated message to packages moved to product-editor package. diff --git a/packages/js/components/changelog/update-refactor-currency-context b/packages/js/components/changelog/update-refactor-currency-context new file mode 100644 index 00000000000..80984834ed2 --- /dev/null +++ b/packages/js/components/changelog/update-refactor-currency-context @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Small tweak to update reference to currencyContext component. diff --git a/packages/js/components/changelog/update-select_tree_dropdown b/packages/js/components/changelog/update-select_tree_dropdown new file mode 100644 index 00000000000..5c9d5b64dc6 --- /dev/null +++ b/packages/js/components/changelog/update-select_tree_dropdown @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Update select tree control dropdown menu for custom slot fill support for display within Modals diff --git a/packages/js/components/changelog/update-tree-item b/packages/js/components/changelog/update-tree-item new file mode 100644 index 00000000000..9473f708688 --- /dev/null +++ b/packages/js/components/changelog/update-tree-item @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +TreeControl: Fix a bug where items with children were not selected in single mode and fix a bug where navigation between items with tab was not working properly. diff --git a/packages/js/components/changelog/update-webpack-invalid-export-error b/packages/js/components/changelog/update-webpack-invalid-export-error new file mode 100644 index 00000000000..d844dbd186f --- /dev/null +++ b/packages/js/components/changelog/update-webpack-invalid-export-error @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update webpack config to use @woocommerce/internal-style-build's parser config diff --git a/packages/js/components/composer.json b/packages/js/components/composer.json index 9bb6df42457..002cbd95bab 100644 --- a/packages/js/components/composer.json +++ b/packages/js/components/composer.json @@ -5,7 +5,7 @@ "license": "GPL-3.0-or-later", "minimum-stability": "dev", "require-dev": { - "automattic/jetpack-changelogger": "3.1.3" + "automattic/jetpack-changelogger": "3.3.0" }, "config": { "platform": { diff --git a/packages/js/components/composer.lock b/packages/js/components/composer.lock index 87b9fcf1a87..4324a3af9b4 100644 --- a/packages/js/components/composer.lock +++ b/packages/js/components/composer.lock @@ -4,32 +4,32 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "75af54f4e83b1e2c7c96371c3288e355", + "content-hash": "0e715b7322bdb353060f76a3279195e1", "packages": [], "packages-dev": [ { "name": "automattic/jetpack-changelogger", - "version": "v3.1.3", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/Automattic/jetpack-changelogger.git", - "reference": "cdd256d8ba6369f82d9377de7e9e2598e3e16ae0" + "reference": "8f63c829b8d1b0d7b1d5de93510d78523ed18959" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Automattic/jetpack-changelogger/zipball/cdd256d8ba6369f82d9377de7e9e2598e3e16ae0", - "reference": "cdd256d8ba6369f82d9377de7e9e2598e3e16ae0", + "url": "https://api.github.com/repos/Automattic/jetpack-changelogger/zipball/8f63c829b8d1b0d7b1d5de93510d78523ed18959", + "reference": "8f63c829b8d1b0d7b1d5de93510d78523ed18959", "shasum": "" }, "require": { "php": ">=5.6", - "symfony/console": "^3.4 || ^5.2", - "symfony/process": "^3.4 || ^5.2", + "symfony/console": "^3.4 || ^5.2 || ^6.0", + "symfony/process": "^3.4 || ^5.2 || ^6.0", "wikimedia/at-ease": "^1.2 || ^2.0" }, "require-dev": { "wikimedia/testing-access-wrapper": "^1.0 || ^2.0", - "yoast/phpunit-polyfills": "1.0.3" + "yoast/phpunit-polyfills": "1.0.4" }, "bin": [ "bin/changelogger" @@ -38,7 +38,7 @@ "extra": { "autotagger": true, "branch-alias": { - "dev-trunk": "3.1.x-dev" + "dev-trunk": "3.3.x-dev" }, "mirror-repo": "Automattic/jetpack-changelogger", "version-constants": { @@ -60,9 +60,9 @@ ], "description": "Jetpack Changelogger tool. Allows for managing changelogs by dropping change files into a changelog directory with each PR.", "support": { - "source": "https://github.com/Automattic/jetpack-changelogger/tree/v3.1.3" + "source": "https://github.com/Automattic/jetpack-changelogger/tree/v3.3.0" }, - "time": "2022-06-21T07:31:56+00:00" + "time": "2022-12-26T13:49:01+00:00" }, { "name": "psr/log", @@ -204,12 +204,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "6637e62480b60817b9a6984154a533e8e64c6bd5" + "reference": "1a692492190773c5310bc7877cb590c04c2f05be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/6637e62480b60817b9a6984154a533e8e64c6bd5", - "reference": "6637e62480b60817b9a6984154a533e8e64c6bd5", + "url": "https://api.github.com/repos/symfony/debug/zipball/1a692492190773c5310bc7877cb590c04c2f05be", + "reference": "1a692492190773c5310bc7877cb590c04c2f05be", "shasum": "" }, "require": { @@ -249,7 +249,7 @@ "description": "Provides tools to ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/debug/tree/v4.4.41" + "source": "https://github.com/symfony/debug/tree/v4.4.44" }, "funding": [ { @@ -266,7 +266,7 @@ } ], "abandoned": "symfony/error-handler", - "time": "2022-04-12T15:19:55+00:00" + "time": "2022-07-28T16:29:46+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -274,12 +274,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { @@ -295,7 +295,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -334,7 +334,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -350,7 +350,7 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/process", diff --git a/packages/js/components/jest.config.json b/packages/js/components/jest.config.json index 91c0faef97f..3d8108048f6 100644 --- a/packages/js/components/jest.config.json +++ b/packages/js/components/jest.config.json @@ -1,4 +1,4 @@ { "rootDir": "./src", - "preset": "../../internal-js-tests/jest.config.js" + "preset": "../node_modules/@woocommerce/internal-js-tests/jest-preset.js" } diff --git a/packages/js/components/package.json b/packages/js/components/package.json index 3955a228b76..ab931494b8e 100644 --- a/packages/js/components/package.json +++ b/packages/js/components/package.json @@ -1,12 +1,12 @@ { "name": "@woocommerce/components", - "version": "11.1.0", + "version": "12.0.0", "description": "UI components for WooCommerce.", "author": "Automattic", "license": "GPL-3.0-or-later", "engines": { - "node": "^16.13.1", - "pnpm": "^7.13.3" + "node": "^16.14.1", + "pnpm": "^8.3.1" }, "keywords": [ "wordpress", @@ -36,33 +36,35 @@ "@types/wordpress__block-editor": "^7.0.0", "@types/wordpress__block-library": "^2.6.1", "@types/wordpress__blocks": "^11.0.7", + "@types/wordpress__components": "^19.10.3", "@types/wordpress__rich-text": "^3.4.6", "@woocommerce/csv-export": "workspace:*", "@woocommerce/currency": "workspace:*", "@woocommerce/data": "workspace:*", "@woocommerce/date": "workspace:*", "@woocommerce/navigation": "workspace:*", - "@wordpress/a11y": "3.5.0", - "@wordpress/api-fetch": "^6.0.1", + "@wordpress/a11y": "wp-6.0", + "@wordpress/api-fetch": "wp-6.0", + "@wordpress/base-styles": "wp-6.0", "@wordpress/block-editor": "^9.8.0", "@wordpress/block-library": "^7.16.0", "@wordpress/blocks": "^11.18.0", - "@wordpress/components": "^19.5.0", - "@wordpress/compose": "^5.1.2", - "@wordpress/core-data": "^4.2.1", - "@wordpress/date": "^4.3.1", - "@wordpress/deprecated": "^3.3.1", - "@wordpress/dom": "^3.3.2", - "@wordpress/element": "^4.1.1", - "@wordpress/hooks": "^3.5.0", - "@wordpress/html-entities": "^3.3.1", - "@wordpress/i18n": "^4.3.1", - "@wordpress/icons": "^8.1.0", - "@wordpress/keyboard-shortcuts": "^3.17.0", - "@wordpress/keycodes": "^3.3.1", - "@wordpress/media-utils": "^4.6.0", - "@wordpress/rich-text": "^5.17.0", - "@wordpress/url": "^3.4.1", + "@wordpress/components": "wp-6.0", + "@wordpress/compose": "wp-6.0", + "@wordpress/core-data": "wp-6.0", + "@wordpress/date": "wp-6.0", + "@wordpress/deprecated": "wp-6.0", + "@wordpress/dom": "wp-6.0", + "@wordpress/element": "wp-6.0", + "@wordpress/hooks": "wp-6.0", + "@wordpress/html-entities": "wp-6.0", + "@wordpress/i18n": "wp-6.0", + "@wordpress/icons": "wp-6.0", + "@wordpress/keyboard-shortcuts": "wp-6.0", + "@wordpress/keycodes": "wp-6.0", + "@wordpress/media-utils": "wp-6.0", + "@wordpress/rich-text": "wp-6.0", + "@wordpress/url": "wp-6.0", "@wordpress/viewport": "^4.1.2", "classnames": "^2.3.1", "core-js": "^3.21.1", @@ -74,7 +76,7 @@ "d3-shape": "^1.3.7", "d3-time-format": "^2.3.0", "dompurify": "^2.3.6", - "downshift": "^6.1.9", + "downshift": "^6.1.12", "emoji-flags": "^1.3.0", "gridicons": "^3.4.0", "memoize-one": "^6.0.0", @@ -85,7 +87,9 @@ "react-transition-group": "^4.4.2" }, "peerDependencies": { - "@wordpress/data": "^6.2.1", + "@types/react": "^17.0.2", + "@types/react-dom": "^17.0.2", + "@wordpress/data": "wp-6.0", "lodash": "^4.17.0", "react": "^17.0.2", "react-dom": "^17.0.2" @@ -111,30 +115,34 @@ "@testing-library/dom": "^8.11.3", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.3", + "@testing-library/react-hooks": "^8.0.1", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.4.1", "@types/lodash": "^4.14.184", - "@types/react": "^17.0.0", + "@types/prop-types": "^15.7.4", "@types/testing-library__jest-dom": "^5.14.3", - "@types/wordpress__components": "^19.10.1", + "@types/uuid": "^8.3.0", + "@types/wordpress__components": "^19.10.3", "@types/wordpress__data": "^6.0.0", "@types/wordpress__media-utils": "^3.0.0", "@types/wordpress__viewport": "^2.5.4", "@woocommerce/eslint-plugin": "workspace:*", + "@woocommerce/internal-js-tests": "workspace:*", "@woocommerce/internal-style-build": "workspace:*", - "@wordpress/browserslist-config": "^4.1.1", + "@wordpress/browserslist-config": "wp-6.0", "@wordpress/scripts": "^12.6.1", "concurrently": "^7.0.0", "css-loader": "^3.6.0", - "eslint": "^8.12.0", + "eslint": "^8.32.0", "jest": "^27.5.1", "jest-cli": "^27.5.1", - "postcss-loader": "^3.0.0", + "postcss": "^8.4.7", + "postcss-loader": "^4.3.0", "react": "^17.0.2", "rimraf": "^3.0.2", "sass-loader": "^10.2.1", "ts-jest": "^27.1.3", - "typescript": "^4.8.3", + "uuid": "^8.3.0", "webpack": "^5.70.0", "webpack-cli": "^3.3.12" }, @@ -146,12 +154,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" }, @@ -160,5 +168,13 @@ "pnpm lint:fix", "pnpm test-staged" ] + }, + "pnpm": { + "overrides": { + "@types/react": "^17.0.2", + "@types/react-dom": "^17.0.2", + "react": "^17.0.2", + "react-dom": "^17.0.2" + } } } diff --git a/packages/js/components/src/abbreviated-card/test/__snapshots__/index.js.snap b/packages/js/components/src/abbreviated-card/test/__snapshots__/index.js.snap index 4d273e3095f..18b50eef22f 100644 --- a/packages/js/components/src/abbreviated-card/test/__snapshots__/index.js.snap +++ b/packages/js/components/src/abbreviated-card/test/__snapshots__/index.js.snap @@ -3,7 +3,7 @@ exports[`AbbreviatedCard it renders correctly 1`] = `
@@ -11,7 +11,7 @@ exports[`AbbreviatedCard it renders correctly 1`] = ` class="css-mgwsf4-View-Content em57xhy0" >
diff --git a/packages/js/components/src/advanced-filters/README.md b/packages/js/components/src/advanced-filters/README.md index 72da82a2994..9d1c3bff59b 100644 --- a/packages/js/components/src/advanced-filters/README.md +++ b/packages/js/components/src/advanced-filters/README.md @@ -4,14 +4,14 @@ Displays a configurable set of filters which can modify query parameters. Displa ## Usage -Below is a config example complete with translation strings. Advanced filters makes use of [interpolateComponents](https://github.com/Automattic/interpolate-components#readme) to organize sentence structure, resulting in a filter visually represented as a sentence fragment in any language. +Below is a config example complete with translation strings. Advanced filters makes use of [createInterpolateElement](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-element/#createinterpolateelement) to organize sentence structure, resulting in a filter visually represented as a sentence fragment in any language. ```js const config = { title: __( // A sentence describing filters for Orders // See screen shot for context: https://cloudup.com/cSsUY9VeCVJ - 'Orders Match {{select /}} Filters', + 'Orders Match - onFilterChange( { - property: 'rule', - value: selectedValue, - } ) - } - aria-label={ labels.rule } - /> - ), - filter: ( -
- { ! Array.isArray( value ) || - ! value.length || - selectedAttribute.length ? ( - { - setSelectedAttribute( - attr ? [ attr ] : [] - ); - setSelectedAttributeTerm( '' ); - onFilterChange( { - property: 'value', - value: [ - attr && attr.key, - ].filter( Boolean ), - } ); - } } - type="attributes" - placeholder={ __( - 'Attribute name', - 'woocommerce' - ) } - multiple={ false } - selected={ selectedAttribute } - inlineTags - aria-label={ __( - 'Attribute name', - 'woocommerce' - ) } - /> + { createInterpolateElement( labels.title, { + title: , + rule: ( + Filters', filters: { status: { labels: { add: 'Order Status', remove: 'Remove order status filter', rule: 'Select an order status filter match', - title: '{{title}}Order Status{{/title}} {{rule /}} {{filter /}}', + title: 'Order Status ', filter: 'Select an order status', }, rules: [ @@ -63,7 +63,7 @@ const advancedFilters = { placeholder: 'Search products', remove: 'Remove products filter', rule: 'Select a product filter match', - title: '{{title}}Product{{/title}} {{rule /}} {{filter /}}', + title: 'Product ', filter: 'Select products', }, rules: [ @@ -87,7 +87,7 @@ const advancedFilters = { add: 'Customer type', remove: 'Remove customer filter', rule: 'Select a customer filter match', - title: '{{title}}Customer is{{/title}} {{filter /}}', + title: 'Customer is ', filter: 'Select a customer type', }, input: { @@ -104,7 +104,7 @@ const advancedFilters = { add: 'Item Quantity', remove: 'Remove item quantity filter', rule: 'Select an item quantity filter match', - title: '{{title}}Item Quantity is{{/title}} {{rule /}} {{filter /}}', + title: 'Item Quantity is ', }, rules: [ { @@ -129,7 +129,7 @@ const advancedFilters = { add: 'Subtotal', remove: 'Remove subtotal filter', rule: 'Select a subtotal filter match', - title: '{{title}}Subtotal is{{/title}} {{rule /}} {{filter /}}', + title: 'Subtotal is ', }, rules: [ { @@ -155,7 +155,7 @@ const advancedFilters = { add: 'Date', remove: 'Remove date filter', rule: 'Select a date filter match', - title: '{{title}}Date{{/title}} {{rule /}} {{filter /}}', + title: 'Date ', filter: 'Select a transaction date', }, rules: [ diff --git a/packages/js/components/src/advanced-filters/test/advanced-filters.test.js b/packages/js/components/src/advanced-filters/test/advanced-filters.test.js index cfa098fcd43..a6ffad0bd2f 100644 --- a/packages/js/components/src/advanced-filters/test/advanced-filters.test.js +++ b/packages/js/components/src/advanced-filters/test/advanced-filters.test.js @@ -34,14 +34,14 @@ const CURRENCY = { }; const advancedFiltersConfig = { - title: 'Orders Match {{select /}} Filters', + title: 'Orders Match { - inputRef.current = node; - ( - inputProps.ref as unknown as ( - node: HTMLInputElement | null - ) => void - )( node ); - } } - /> +
+ { children } +
+ { + if ( typeof inputProps.ref === 'function' ) { + inputRef.current = node; + ( + inputProps.ref as unknown as ( + node: HTMLInputElement | null + ) => void + )( node ); + } + } } + /> +
- + { suffix && ( +
+ { suffix } +
+ ) } + { showToggleButton && ( + + ) }
); }; diff --git a/packages/js/components/src/experimental-select-control/hooks/use-async-filter.tsx b/packages/js/components/src/experimental-select-control/hooks/use-async-filter.tsx new file mode 100644 index 00000000000..8672acfa535 --- /dev/null +++ b/packages/js/components/src/experimental-select-control/hooks/use-async-filter.tsx @@ -0,0 +1,79 @@ +/** + * External dependencies + */ +import { Spinner } from '@wordpress/components'; +import { useDebounce } from '@wordpress/compose'; +import { useCallback, useState, createElement } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { SelectControlProps } from '../select-control'; +import { SuffixIcon } from '../suffix-icon'; + +export const DEFAULT_DEBOUNCE_TIME = 250; + +export default function useAsyncFilter< T >( { + filter, + onFilterStart, + onFilterEnd, + onFilterError, + debounceTime, +}: UseAsyncFilterInput< T > ): UseAsyncFilterOutput< T > { + const [ isFetching, setIsFetching ] = useState( false ); + + const handleInputChange = useCallback( + function handleInputChangeCallback( value?: string ) { + if ( typeof filter === 'function' ) { + if ( typeof onFilterStart === 'function' ) + onFilterStart( value ); + + setIsFetching( true ); + + filter( value ) + .then( ( filteredItems ) => { + if ( typeof onFilterEnd === 'function' ) + onFilterEnd( filteredItems, value ); + } ) + .catch( ( error: Error ) => { + if ( typeof onFilterError === 'function' ) + onFilterError( error, value ); + } ) + .finally( () => { + setIsFetching( false ); + } ); + } + }, + [ filter, onFilterStart, onFilterEnd, onFilterError ] + ); + + return { + isFetching, + suffix: + isFetching === true ? ( + } /> + ) : undefined, + getFilteredItems: ( items ) => items, + onInputChange: useDebounce( + handleInputChange, + typeof debounceTime === 'number' + ? debounceTime + : DEFAULT_DEBOUNCE_TIME + ), + }; +} + +export type UseAsyncFilterInput< T > = { + filter( value?: string ): Promise< T[] >; + onFilterStart?( value?: string ): void; + onFilterEnd?( filteredItems: T[], value?: string ): void; + onFilterError?( error: Error, value?: string ): void; + debounceTime?: number; +}; + +export type UseAsyncFilterOutput< T > = Pick< + SelectControlProps< T >, + 'suffix' | 'onInputChange' | 'getFilteredItems' +> & { + isFetching: boolean; +}; diff --git a/packages/js/components/src/experimental-select-control/index.ts b/packages/js/components/src/experimental-select-control/index.ts index d30d0825353..d328ae7fccd 100644 --- a/packages/js/components/src/experimental-select-control/index.ts +++ b/packages/js/components/src/experimental-select-control/index.ts @@ -1 +1,2 @@ export * from './select-control'; +export { default as useAsyncFilter } from './hooks/use-async-filter'; diff --git a/packages/js/components/src/experimental-select-control/menu-item.tsx b/packages/js/components/src/experimental-select-control/menu-item.tsx index 2200ceba2ef..ef3dbbd465b 100644 --- a/packages/js/components/src/experimental-select-control/menu-item.tsx +++ b/packages/js/components/src/experimental-select-control/menu-item.tsx @@ -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 (
  • diff --git a/packages/js/components/src/experimental-select-control/menu.tsx b/packages/js/components/src/experimental-select-control/menu.tsx index 60a8cbb6e4e..2587e8422f2 100644 --- a/packages/js/components/src/experimental-select-control/menu.tsx +++ b/packages/js/components/src/experimental-select-control/menu.tsx @@ -10,6 +10,7 @@ import { useState, createPortal, Children, + useLayoutEffect, } from '@wordpress/element'; /** @@ -22,6 +23,8 @@ type MenuProps = { getMenuProps: getMenuPropsType; isOpen: boolean; className?: string; + position?: Popover.Position; + scrollIntoViewOnOpen?: boolean; }; export const Menu = ( { @@ -29,56 +32,73 @@ export const Menu = ( { getMenuProps, isOpen, className, + position = 'bottom right', + scrollIntoViewOnOpen = false, }: MenuProps ) => { const [ boundingRect, setBoundingRect ] = useState< DOMRect >(); const selectControlMenuRef = useRef< HTMLDivElement >( null ); - useEffect( () => { - if ( selectControlMenuRef.current?.parentElement ) { + useLayoutEffect( () => { + if ( + selectControlMenuRef.current?.parentElement && + selectControlMenuRef.current?.parentElement.clientWidth > 0 + ) { setBoundingRect( selectControlMenuRef.current.parentElement.getBoundingClientRect() ); } - }, [ selectControlMenuRef.current ] ); + }, [ + selectControlMenuRef.current, + selectControlMenuRef.current?.clientWidth, + ] ); + + // 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 (
    - 0, - } - ) } - position="bottom center" - animate={ false } - > -
      - // Fix to prevent select control dropdown from closing when selecting within the Popover. - e.stopPropagation() - } +
      + 0, + } + ) } + position={ position } + animate={ false } > - { isOpen && children } -
    -
    +
      + // Fix to prevent select control dropdown from closing when selecting within the Popover. + e.stopPropagation() + } + > + { isOpen && children } +
    + +
  • ); /* eslint-enable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events */ diff --git a/packages/js/components/src/experimental-select-control/select-control.scss b/packages/js/components/src/experimental-select-control/select-control.scss index 129331685ac..cf71dadd8cd 100644 --- a/packages/js/components/src/experimental-select-control/select-control.scss +++ b/packages/js/components/src/experimental-select-control/select-control.scss @@ -2,6 +2,7 @@ @import './menu.scss'; @import './menu-item.scss'; @import './selected-items.scss'; +@import './suffix-icon.scss'; .woocommerce-experimental-select-control { position: relative; diff --git a/packages/js/components/src/experimental-select-control/select-control.tsx b/packages/js/components/src/experimental-select-control/select-control.tsx index 8545e652112..7957e980d55 100644 --- a/packages/js/components/src/experimental-select-control/select-control.tsx +++ b/packages/js/components/src/experimental-select-control/select-control.tsx @@ -7,6 +7,7 @@ import { UseComboboxState, UseComboboxStateChangeOptions, useMultipleSelection, + GetInputPropsOptions, } from 'downshift'; import { useState, @@ -14,6 +15,7 @@ import { createElement, Fragment, } from '@wordpress/element'; +import { search } from '@wordpress/icons'; /** * Internal dependencies @@ -28,16 +30,17 @@ import { SelectedItems } from './selected-items'; import { ComboBox } from './combo-box'; import { Menu } from './menu'; import { MenuItem } from './menu-item'; +import { SuffixIcon } from './suffix-icon'; import { defaultGetItemLabel, defaultGetItemValue, defaultGetFilteredItems, } from './utils'; -type SelectControlProps< ItemType > = { +export type SelectControlProps< ItemType > = { children?: ChildrenType< ItemType >; items: ItemType[]; - label: string; + label: string | JSX.Element; getItemLabel?: getItemLabelType< ItemType >; getItemValue?: getItemValueType< ItemType >; getFilteredItems?: ( @@ -63,6 +66,18 @@ type SelectControlProps< ItemType > = { selected: ItemType | ItemType[] | null; className?: string; 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 + * current downshift version get updated. + * + * @see https://www.downshift-js.com/use-multiple-selection#usage-with-combobox + * @default false + */ + __experimentalOpenMenuOnFocus?: boolean; }; export const selectControlStateChangeTypes = useCombobox.stateChangeTypes; @@ -107,6 +122,10 @@ function SelectControl< ItemType = DefaultItemType >( { selected, className, disabled, + inputProps = {}, + suffix = , + showToggleButton = false, + __experimentalOpenMenuOnFocus = false, }: SelectControlProps< ItemType > ) { const [ isFocused, setIsFocused ] = useState( false ); const [ inputValue, setInputValue ] = useState( '' ); @@ -137,12 +156,13 @@ function SelectControl< ItemType = DefaultItemType >( { } setInputValue( getItemLabel( singleSelectedItem ) ); - }, [ singleSelectedItem ] ); + }, [ getItemLabel, multiple, singleSelectedItem ] ); const { isOpen, getLabelProps, getMenuProps, + getToggleButtonProps, getInputProps, getComboboxProps, highlightedIndex, @@ -157,8 +177,13 @@ function SelectControl< ItemType = DefaultItemType >( { items: filteredItems, selectedItem: multiple ? null : singleSelectedItem, itemToString: getItemLabel, - onSelectedItemChange: ( { selectedItem } ) => - selectedItem && onSelect( selectedItem ), + onSelectedItemChange: ( { selectedItem } ) => { + if ( selectedItem ) { + onSelect( selectedItem ); + } else if ( singleSelectedItem ) { + onRemove( singleSelectedItem ); + } + }, onInputValueChange: ( { inputValue: value, ...changes } ) => { if ( value !== undefined ) { setInputValue( value ); @@ -173,8 +198,13 @@ function SelectControl< ItemType = DefaultItemType >( { // Set input back to selected item if there is a selected item, blank otherwise. newChanges = { ...changes, + selectedItem: + ! changes.inputValue?.length && ! multiple + ? null + : changes.selectedItem, inputValue: changes.selectedItem === state.selectedItem && + changes.inputValue?.length && ! multiple ? getItemLabel( comboboxSingleSelectedItem ) : '', @@ -239,6 +269,7 @@ function SelectControl< ItemType = DefaultItemType >( { { /* eslint-enable jsx-a11y/label-has-for */ } ( { onFocus: () => { setIsFocused( true ); onFocus( { inputValue } ); + if ( __experimentalOpenMenuOnFocus ) { + openMenu(); + } }, onBlur: () => setIsFocused( false ), placeholder, disabled, + ...inputProps, } ) } + suffix={ suffix } + showToggleButton={ showToggleButton } > <> { children( { diff --git a/packages/js/components/src/experimental-select-control/stories/index.tsx b/packages/js/components/src/experimental-select-control/stories/index.tsx index c13f9c88ebd..cb1706d98d3 100644 --- a/packages/js/components/src/experimental-select-control/stories/index.tsx +++ b/packages/js/components/src/experimental-select-control/stories/index.tsx @@ -8,18 +8,24 @@ import { SlotFillProvider, Spinner, } from '@wordpress/components'; -import React from 'react'; +import React, { useCallback } from 'react'; import { createElement, useState } from '@wordpress/element'; +import { tag } from '@wordpress/icons'; /** * Internal dependencies */ import { SelectedType, DefaultItemType, getItemLabelType } from '../types'; import { MenuItem } from '../menu-item'; -import { SelectControl, selectControlStateChangeTypes } from '../'; +import { + SelectControl, + selectControlStateChangeTypes, + useAsyncFilter, +} from '../'; import { Menu, MenuSlot } from '../menu'; +import { SuffixIcon } from '../suffix-icon'; -const sampleItems = [ +const sampleItems: DefaultItemType[] = [ { value: 'apple', label: 'Apple' }, { value: 'pear', label: 'Pear' }, { value: 'orange', label: 'Orange' }, @@ -131,35 +137,121 @@ export const FuzzyMatching: React.FC = () => { export const Async: React.FC = () => { const [ selectedItem, setSelectedItem ] = - useState< SelectedType< DefaultItemType > >( null ); + useState< DefaultItemType | null >( null ); const [ fetchedItems, setFetchedItems ] = useState< DefaultItemType[] >( [] ); - const [ isFetching, setIsFetching ] = useState( false ); - const fetchItems = ( value: string | undefined ) => { - setIsFetching( true ); - setFetchedItems( [] ); - setTimeout( () => { - const results = sampleItems.sort( () => 0.5 - Math.random() ); - setFetchedItems( results ); - setIsFetching( false ); - }, 1500 ); - }; + const filter = useCallback( + ( value = '' ) => + new Promise< DefaultItemType[] >( ( resolve ) => { + setTimeout( () => { + const filteredItems = [ ...sampleItems ] + .sort( ( a, b ) => a.label.localeCompare( b.label ) ) + .filter( ( { label } ) => + label.toLowerCase().includes( value.toLowerCase() ) + ); + resolve( filteredItems ); + }, 1500 ); + } ), + [ selectedItem ] + ); + + const { isFetching, ...selectProps } = useAsyncFilter< DefaultItemType >( { + filter, + onFilterStart() { + setFetchedItems( [] ); + }, + onFilterEnd( filteredItems ) { + setFetchedItems( filteredItems ); + }, + } ); return ( <> - + { ...selectProps } label="Async" - getFilteredItems={ ( allItems ) => { - return allItems; - } } items={ fetchedItems } - onInputChange={ fetchItems } selected={ selectedItem } - onSelect={ ( item ) => setSelectedItem( item ) } - onRemove={ () => setSelectedItem( null ) } placeholder="Start typing..." + onSelect={ setSelectedItem } + onRemove={ () => setSelectedItem( null ) } + > + { ( { + items, + isOpen, + highlightedIndex, + getItemProps, + getMenuProps, + } ) => { + return ( + + { isFetching ? ( + + ) : ( + items.map( ( item, index: number ) => ( + + { item.label } + + ) ) + ) } + + ); + } } + + + ); +}; + +export const AsyncWithoutListeningFilterEvents: React.FC = () => { + const [ selectedItem, setSelectedItem ] = + useState< DefaultItemType | null >( null ); + const [ fetchedItems, setFetchedItems ] = useState< DefaultItemType[] >( + [] + ); + + const filter = useCallback( + async ( value = '' ) => { + setFetchedItems( [] ); + return new Promise< DefaultItemType[] >( ( resolve ) => { + setTimeout( () => { + const filteredItems = [ ...sampleItems ] + .sort( ( a, b ) => a.label.localeCompare( b.label ) ) + .filter( ( { label } ) => + label.toLowerCase().includes( value.toLowerCase() ) + ); + + resolve( filteredItems ); + }, 1500 ); + } ).then( ( filteredItems ) => { + setFetchedItems( filteredItems ); + return filteredItems; + } ); + }, + [ selectedItem ] + ); + + const { isFetching, ...selectProps } = useAsyncFilter< DefaultItemType >( { + filter, + } ); + + return ( + <> + + { ...selectProps } + label="Async" + items={ fetchedItems } + selected={ selectedItem } + placeholder="Start typing..." + onSelect={ setSelectedItem } + onRemove={ () => setSelectedItem( null ) } > { ( { items, @@ -410,6 +502,95 @@ export const SingleWithinModalUsingBodyDropdownPlacement: React.FC = () => { ); }; +export const DefaultSuffix: React.FC = () => { + const [ selected, setSelected ] = useState< + SelectedType< DefaultItemType > + >( sampleItems[ 1 ] ); + + return ( + item && setSelected( item ) } + onRemove={ () => setSelected( null ) } + /> + ); +}; + +export const CustomSuffixIcon: React.FC = () => { + const [ selected, setSelected ] = useState< + SelectedType< DefaultItemType > + >( sampleItems[ 1 ] ); + + return ( + item && setSelected( item ) } + onRemove={ () => setSelected( null ) } + suffix={ } + /> + ); +}; + +export const NoSuffix: React.FC = () => { + const [ selected, setSelected ] = useState< + SelectedType< DefaultItemType > + >( sampleItems[ 1 ] ); + + return ( + item && setSelected( item ) } + onRemove={ () => setSelected( null ) } + suffix={ null } + /> + ); +}; + +export const CustomSuffix: React.FC = () => { + const [ selected, setSelected ] = useState< + SelectedType< DefaultItemType > + >( sampleItems[ 1 ] ); + + return ( + item && setSelected( item ) } + onRemove={ () => setSelected( null ) } + suffix={ +
    + Suffix! +
    + } + /> + ); +}; + +export const ToggleButton: React.FC = () => { + const [ selected, setSelected ] = + useState< SelectedType< DefaultItemType > >(); + + return ( + item && setSelected( item ) } + onRemove={ () => setSelected( null ) } + suffix={ null } + showToggleButton={ true } + __experimentalOpenMenuOnFocus={ true } + /> + ); +}; + export default { title: 'WooCommerce Admin/experimental/SelectControl', component: SelectControl, diff --git a/packages/js/components/src/experimental-select-control/suffix-icon.scss b/packages/js/components/src/experimental-select-control/suffix-icon.scss new file mode 100644 index 00000000000..f007e55d7ef --- /dev/null +++ b/packages/js/components/src/experimental-select-control/suffix-icon.scss @@ -0,0 +1,10 @@ +.woocommerce-experimental-select-control__suffix-icon { + display: flex; + align-items: center; + height: 100%; + padding-right: $gap-smaller; + + .components-spinner { + margin: 0; + } +} diff --git a/packages/js/components/src/experimental-select-control/suffix-icon.tsx b/packages/js/components/src/experimental-select-control/suffix-icon.tsx new file mode 100644 index 00000000000..f1077fb08de --- /dev/null +++ b/packages/js/components/src/experimental-select-control/suffix-icon.tsx @@ -0,0 +1,17 @@ +/** + * External dependencies + */ +import { createElement } from 'react'; +import { Icon } from '@wordpress/icons'; + +type SuffixIconProps = { + icon: JSX.Element; +}; + +export const SuffixIcon = ( { icon }: SuffixIconProps ) => { + return ( +
    + +
    + ); +}; diff --git a/packages/js/components/src/experimental-select-control/test/index.tsx b/packages/js/components/src/experimental-select-control/test/index.tsx new file mode 100644 index 00000000000..b93d64a9c07 --- /dev/null +++ b/packages/js/components/src/experimental-select-control/test/index.tsx @@ -0,0 +1,57 @@ +/** + * External dependencies + */ +import { render } from '@testing-library/react'; +import { createElement } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { SelectControl } from '../'; + +describe( 'SelectControl', () => { + it( 'should render the default suffix if none is specified', () => { + const { container } = render( + + ); + + // We can't really determine if the correct suffix icon is being used + // without checking against the SVG path, which would be brittle; + // so, we just check if any suffix icon has been rendered + expect( + container.querySelector( + '.woocommerce-experimental-select-control__suffix-icon' + ) + ).toBeInTheDocument(); + } ); + + it( 'should render a custom suffix if one is specified', () => { + const { getByText } = render( + custom suffix
    } + /> + ); + + expect( getByText( 'custom suffix' ) ).toBeInTheDocument(); + } ); + + it( 'should render no suffix if null is specified', () => { + const { container } = render( + + ); + + expect( + container.querySelector( + '.woocommerce-experimental-select-control__suffix' + ) + ).not.toBeInTheDocument(); + } ); +} ); diff --git a/packages/js/components/src/experimental-select-control/test/use-async-filter.spec.ts b/packages/js/components/src/experimental-select-control/test/use-async-filter.spec.ts new file mode 100644 index 00000000000..846f407ca3f --- /dev/null +++ b/packages/js/components/src/experimental-select-control/test/use-async-filter.spec.ts @@ -0,0 +1,128 @@ +/** + * External dependencies + */ +import { act, renderHook } from '@testing-library/react-hooks'; +import { useDebounce } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import { useAsyncFilter } from '../'; + +jest.mock( '@wordpress/compose', () => ( { + ...jest.requireActual( '@wordpress/compose' ), + useDebounce: jest.fn( ( cb: CallableFunction ) => cb ), +} ) ); + +describe( 'useAsyncFilter', () => { + const filter = jest.fn(); + const onFilterStart = jest.fn(); + const onFilterEnd = jest.fn(); + const onFilterError = jest.fn(); + + afterEach( () => { + jest.clearAllMocks(); + } ); + + it( 'should filter the items successfully', async () => { + const filteredItems: string[] = []; + + filter.mockResolvedValue( filteredItems ); + + const { result } = renderHook( () => + useAsyncFilter( { + filter, + } ) + ); + + const inputValue = 'Apple'; + + await act( async () => { + if ( result.current.onInputChange ) + result.current.onInputChange( inputValue, {} ); + } ); + + expect( useDebounce ).toHaveBeenCalledWith( + expect.any( Function ), + 250 + ); + expect( filter ).toHaveBeenCalledWith( inputValue ); + } ); + + it( 'should trigger onFilterStart at the begining of the filtering', async () => { + const filteredItems: string[] = []; + + onFilterStart.mockImplementation( ( value = '' ) => { + expect( filter ).not.toHaveBeenCalledWith( value ); + } ); + + filter.mockImplementation( ( value = '' ) => { + expect( onFilterStart ).toHaveBeenCalledWith( value ); + return Promise.resolve( filteredItems ); + } ); + + const { result } = renderHook( () => + useAsyncFilter( { + filter, + onFilterStart, + } ) + ); + + const inputValue = 'Apple'; + + await act( async () => { + if ( result.current.onInputChange ) + result.current.onInputChange( inputValue, {} ); + } ); + + expect( filter ).toHaveBeenCalledWith( inputValue ); + } ); + + it( 'should trigger onFilterEnd when filtering is fullfiled', async () => { + const filteredItems: string[] = []; + + filter.mockResolvedValue( filteredItems ); + + const { result } = renderHook( () => + useAsyncFilter( { + filter, + onFilterEnd, + onFilterError, + } ) + ); + + const inputValue = 'Apple'; + + await act( async () => { + if ( result.current.onInputChange ) + result.current.onInputChange( inputValue, {} ); + } ); + + expect( onFilterEnd ).toHaveBeenCalledWith( filteredItems, inputValue ); + expect( onFilterError ).not.toHaveBeenCalled(); + } ); + + it( 'should trigger onFilterError when filtering is rejected', async () => { + const error = new Error(); + + filter.mockRejectedValue( error ); + + const { result } = renderHook( () => + useAsyncFilter( { + filter, + onFilterEnd, + onFilterError, + } ) + ); + + const inputValue = 'Apple'; + + await act( async () => { + if ( result.current.onInputChange ) + result.current.onInputChange( inputValue, {} ); + } ); + + expect( onFilterEnd ).not.toHaveBeenCalled(); + expect( onFilterError ).toHaveBeenCalledWith( error, inputValue ); + } ); +} ); diff --git a/packages/js/components/src/experimental-select-control/types.ts b/packages/js/components/src/experimental-select-control/types.ts index 7d1a85f8cee..331b2d148b8 100644 --- a/packages/js/components/src/experimental-select-control/types.ts +++ b/packages/js/components/src/experimental-select-control/types.ts @@ -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 > = ( diff --git a/packages/js/components/src/experimental-select-tree-control/index.ts b/packages/js/components/src/experimental-select-tree-control/index.ts new file mode 100644 index 00000000000..876360ad549 --- /dev/null +++ b/packages/js/components/src/experimental-select-tree-control/index.ts @@ -0,0 +1,2 @@ +export * from './select-tree'; +export * from './select-tree-menu'; diff --git a/packages/js/components/src/experimental-select-tree-control/select-tree-menu.tsx b/packages/js/components/src/experimental-select-tree-control/select-tree-menu.tsx new file mode 100644 index 00000000000..294ceafef60 --- /dev/null +++ b/packages/js/components/src/experimental-select-tree-control/select-tree-menu.tsx @@ -0,0 +1,154 @@ +/** + * External dependencies + */ +import { Popover, Spinner } from '@wordpress/components'; +import classnames from 'classnames'; +import { + createElement, + useEffect, + useRef, + createPortal, + useLayoutEffect, + useState, +} from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { + LinkedTree, + Tree, + TreeControlProps, +} from '../experimental-tree-control'; + +type MenuProps = { + isOpen: boolean; + isLoading?: boolean; + position?: Popover.Position; + scrollIntoViewOnOpen?: boolean; + items: LinkedTree[]; + treeRef?: React.ForwardedRef< HTMLOListElement >; + onClose?: () => void; +} & Omit< TreeControlProps, 'items' >; + +export const SelectTreeMenu = ( { + isLoading, + isOpen, + className, + position = 'bottom center', + scrollIntoViewOnOpen = false, + items, + treeRef: ref, + onClose = () => {}, + shouldShowCreateButton, + ...props +}: MenuProps ) => { + const [ boundingRect, setBoundingRect ] = useState< DOMRect >(); + const selectControlMenuRef = useRef< HTMLDivElement >( null ); + + useLayoutEffect( () => { + if ( + selectControlMenuRef.current?.parentElement && + selectControlMenuRef.current?.parentElement.clientWidth > 0 + ) { + setBoundingRect( + selectControlMenuRef.current.parentElement.getBoundingClientRect() + ); + } + }, [ + selectControlMenuRef.current, + selectControlMenuRef.current?.clientWidth, + ] ); + + // Scroll the selected item into view when the menu opens. + useEffect( () => { + if ( isOpen && scrollIntoViewOnOpen ) { + selectControlMenuRef.current?.scrollIntoView(); + } + }, [ isOpen, scrollIntoViewOnOpen ] ); + + 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 ); + } ); + }; + + /* 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 ( +
    +
    + 0, + } + ) } + position={ position } + animate={ false } + onFocusOutside={ () => { + onClose(); + } } + > + { isOpen && ( +
    + { isLoading ? ( +
    + +
    + ) : ( + + ) } +
    + ) } +
    +
    +
    + ); + /* eslint-enable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events */ +}; + +export const SelectTreeMenuSlot: React.FC = () => + createPortal( +
    + { /* @ts-expect-error name does exist on PopoverSlot see: https://github.com/WordPress/gutenberg/blob/trunk/packages/components/src/popover/index.tsx#L555 */ } + +
    , + document.body + ); diff --git a/packages/js/components/src/experimental-select-tree-control/select-tree.scss b/packages/js/components/src/experimental-select-tree-control/select-tree.scss new file mode 100644 index 00000000000..8c1c78cf8ca --- /dev/null +++ b/packages/js/components/src/experimental-select-tree-control/select-tree.scss @@ -0,0 +1,19 @@ +.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; +} + +.woocommerce-experimental-select-tree-control__popover-menu { + min-height: 100px; +} diff --git a/packages/js/components/src/experimental-select-tree-control/select-tree.tsx b/packages/js/components/src/experimental-select-tree-control/select-tree.tsx new file mode 100644 index 00000000000..5c276342cc8 --- /dev/null +++ b/packages/js/components/src/experimental-select-tree-control/select-tree.tsx @@ -0,0 +1,154 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/** + * External dependencies + */ +import { createElement, useState } from '@wordpress/element'; +import classNames from 'classnames'; +import { search } from '@wordpress/icons'; +import { useInstanceId } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import { useLinkedTree } from '../experimental-tree-control/hooks/use-linked-tree'; +import { Item, 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'; +import { SelectTreeMenu } from './select-tree-menu'; + +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 = , + placeholder, + isLoading, + onInputChange, + shouldShowCreateButton, + ...props +}: SelectTreeProps ) { + const linkedTree = useLinkedTree( items ); + const menuInstanceId = useInstanceId( + SelectTree, + 'woocommerce-select-tree-control__menu' + ); + + const [ isFocused, setIsFocused ] = useState( false ); + const [ isOpen, setIsOpen ] = useState( false ); + + return ( +
    +
    + + { + if ( ! isOpen ) { + setIsOpen( true ); + } + setIsFocused( true ); + }, + onBlur: ( event ) => { + // if blurring to an element inside the dropdown, don't close it + if ( + isOpen && + ! document + .querySelector( '.' + menuInstanceId ) + ?.contains( event.relatedTarget ) + ) { + setIsOpen( false ); + } + setIsFocused( false ); + }, + onKeyDown: ( event ) => { + setIsOpen( true ); + if ( event.key === 'ArrowDown' ) { + event.preventDefault(); + // focus on the first element from the Popover + ( + document.querySelector( + `.${ menuInstanceId } input, .${ menuInstanceId } button` + ) as HTMLInputElement | HTMLButtonElement + )?.focus(); + } + if ( event.key === 'Tab' ) { + setIsOpen( false ); + } + }, + onChange: ( event ) => + onInputChange && + onInputChange( event.target.value ), + placeholder, + } } + suffix={ suffix } + > + item?.label || '' } + getItemValue={ ( item ) => item?.value || '' } + onRemove={ ( item ) => { + if ( ! Array.isArray( item ) && props.onRemove ) { + props.onRemove( item ); + setIsOpen( false ); + } + } } + getSelectedItemProps={ () => ( {} ) } + /> + +
    + setIsOpen( false ) } + /> +
    + ); +}; diff --git a/packages/js/components/src/experimental-select-tree-control/stories/index.tsx b/packages/js/components/src/experimental-select-tree-control/stories/index.tsx new file mode 100644 index 00000000000..024f1e68e9e --- /dev/null +++ b/packages/js/components/src/experimental-select-tree-control/stories/index.tsx @@ -0,0 +1,168 @@ +/** + * External dependencies + */ +import React, { createElement, useState } from 'react'; +import { Button, Modal, SlotFillProvider } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { SelectTree } from '../select-tree'; +import { Item } from '../../experimental-tree-control/types'; +import { SelectTreeMenuSlot } from '../select-tree-menu'; + +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 ( + + ! 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 const SingleWithinModalUsingBodyDropdownPlacement: React.FC = () => { + const [ isOpen, setOpen ] = useState( true ); + const [ value, setValue ] = useState( '' ); + const [ selected, setSelected ] = useState< Item[] >( [] ); + + const items = filterItems( listItems, value ); + + return ( + + Selected: { JSON.stringify( selected ) } + + { isOpen && ( + setOpen( false ) } + > + + ! 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, +}; diff --git a/packages/js/components/src/experimental-select-tree-control/test/select-tree.test.tsx b/packages/js/components/src/experimental-select-tree-control/test/select-tree.test.tsx new file mode 100644 index 00000000000..efb87059c6c --- /dev/null +++ b/packages/js/components/src/experimental-select-tree-control/test/select-tree.test.tsx @@ -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( + + ); + 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( + 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( + + ); + 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( ); + 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( + + ); + queryByPlaceholderText( 'Type here' )?.focus(); + expect( queryAllByRole( 'treeitem' )[ 0 ] ).toHaveAttribute( + 'aria-selected', + 'true' + ); + } ); + + it( 'should show Create "" button', () => { + const { queryByPlaceholderText, queryByText } = render( + true } + /> + ); + queryByPlaceholderText( 'Type here' )?.focus(); + expect( queryByText( 'Create "new item"' ) ).toBeInTheDocument(); + } ); + it( 'should call onCreateNew when Create "" button is clicked', () => { + const mockFn = jest.fn(); + const { queryByPlaceholderText, queryByText } = render( + true } + onCreateNew={ mockFn } + /> + ); + queryByPlaceholderText( 'Type here' )?.focus(); + queryByText( 'Create "new item"' )?.click(); + expect( mockFn ).toBeCalledTimes( 1 ); + } ); +} ); diff --git a/packages/js/components/src/experimental-tree-control/hooks/use-expander.ts b/packages/js/components/src/experimental-tree-control/hooks/use-expander.ts new file mode 100644 index 00000000000..02a9e5e54d3 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/hooks/use-expander.ts @@ -0,0 +1,40 @@ +/** + * External dependencies + */ +import { useEffect, useState } from 'react'; + +/** + * Internal dependencies + */ +import { TreeItemProps } from '../types'; + +export function useExpander( { + shouldItemBeExpanded, + item, +}: Pick< TreeItemProps, 'shouldItemBeExpanded' | 'item' > ) { + const [ isExpanded, setExpanded ] = useState( false ); + + useEffect( () => { + if ( + item.children?.length && + typeof shouldItemBeExpanded === 'function' && + ! isExpanded + ) { + setExpanded( shouldItemBeExpanded( item ) ); + } + }, [ item, shouldItemBeExpanded ] ); + + function onExpand() { + setExpanded( true ); + } + + function onCollapse() { + setExpanded( false ); + } + + function onToggleExpand() { + setExpanded( ( prev ) => ! prev ); + } + + return { isExpanded, onExpand, onCollapse, onToggleExpand }; +} diff --git a/packages/js/components/src/experimental-tree-control/hooks/use-highlighter.ts b/packages/js/components/src/experimental-tree-control/hooks/use-highlighter.ts new file mode 100644 index 00000000000..3a98c2d3509 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/hooks/use-highlighter.ts @@ -0,0 +1,31 @@ +/** + * External dependencies + */ +import { useMemo } from 'react'; + +/** + * Internal dependencies + */ +import { CheckedStatus, TreeItemProps } from '../types'; + +export function useHighlighter( { + item, + multiple, + checkedStatus, + shouldItemBeHighlighted, +}: Pick< TreeItemProps, 'item' | 'multiple' | 'shouldItemBeHighlighted' > & { + checkedStatus: CheckedStatus; +} ) { + const isHighlighted = useMemo( () => { + if ( typeof shouldItemBeHighlighted === 'function' ) { + if ( multiple || item.children.length === 0 ) { + return shouldItemBeHighlighted( item ); + } + } + if ( ! multiple ) { + return checkedStatus === 'checked'; + } + }, [ item, multiple, checkedStatus, shouldItemBeHighlighted ] ); + + return { isHighlighted }; +} diff --git a/packages/js/components/src/experimental-tree-control/hooks/use-keyboard.ts b/packages/js/components/src/experimental-tree-control/hooks/use-keyboard.ts new file mode 100644 index 00000000000..54db7555200 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/hooks/use-keyboard.ts @@ -0,0 +1,178 @@ +/** + * External dependencies + */ + +/** + * Internal dependencies + */ +import { LinkedTree } from '../types'; + +function getFirstChild( + currentHeading: HTMLDivElement +): HTMLLabelElement | null { + const parentTreeItem = currentHeading?.closest< HTMLDivElement >( + '.experimental-woocommerce-tree-item' + ); + const firstSubTreeItem = parentTreeItem?.querySelector( + '.experimental-woocommerce-tree > .experimental-woocommerce-tree-item' + ); + const label = firstSubTreeItem?.querySelector< HTMLLabelElement >( + '.experimental-woocommerce-tree-item__heading > .experimental-woocommerce-tree-item__label' + ); + return label ?? null; +} + +function getFirstAncestor( + currentHeading: HTMLDivElement +): HTMLLabelElement | null { + const parentTree = currentHeading?.closest< HTMLDivElement >( + '.experimental-woocommerce-tree' + ); + const grandParentTreeItem = parentTree?.closest< HTMLDivElement >( + '.experimental-woocommerce-tree-item' + ); + const label = grandParentTreeItem?.querySelector< HTMLLabelElement >( + '.experimental-woocommerce-tree-item__heading > .experimental-woocommerce-tree-item__label' + ); + return label ?? null; +} + +function getAllHeadings( + currentHeading: HTMLDivElement +): NodeListOf< HTMLDivElement > | undefined { + const rootTree = currentHeading.closest< HTMLDivElement >( + '.experimental-woocommerce-tree--level-1' + ); + return rootTree?.querySelectorAll< HTMLDivElement >( + '.experimental-woocommerce-tree-item > .experimental-woocommerce-tree-item__heading' + ); +} + +const step = { + ArrowDown: 1, + ArrowUp: -1, +}; + +function getNextFocusableElement( + currentHeading: HTMLDivElement, + code: 'ArrowDown' | 'ArrowUp' +): HTMLLabelElement | null { + const headingsNodeList = getAllHeadings( currentHeading ); + if ( ! headingsNodeList ) return null; + + let currentHeadingIndex = 0; + for ( const heading of headingsNodeList.values() ) { + if ( heading === currentHeading ) break; + currentHeadingIndex++; + } + if ( + currentHeadingIndex < 0 || + currentHeadingIndex >= headingsNodeList.length + ) { + return null; + } + + const heading = headingsNodeList.item( + currentHeadingIndex + ( step[ code ] ?? 0 ) + ); + return heading?.querySelector< HTMLLabelElement >( + '.experimental-woocommerce-tree-item__label' + ); +} + +function getFirstFocusableElement( + currentHeading: HTMLDivElement +): HTMLLabelElement | null { + const headingsNodeList = getAllHeadings( currentHeading ); + if ( ! headingsNodeList ) return null; + return headingsNodeList + .item( 0 ) + .querySelector< HTMLLabelElement >( + '.experimental-woocommerce-tree-item__label' + ); +} + +function getLastFocusableElement( + currentHeading: HTMLDivElement +): HTMLLabelElement | null { + const headingsNodeList = getAllHeadings( currentHeading ); + if ( ! headingsNodeList ) return null; + return headingsNodeList + .item( headingsNodeList.length - 1 ) + .querySelector< HTMLLabelElement >( + '.experimental-woocommerce-tree-item__label' + ); +} + +export function useKeyboard( { + item, + isExpanded, + onExpand, + onCollapse, + onToggleExpand, + 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' ) { + event.preventDefault(); + if ( item.children.length > 0 ) { + if ( isExpanded ) { + const element = getFirstChild( event.currentTarget ); + return element?.focus(); + } + onExpand(); + } + } + + if ( event.code === 'ArrowLeft' ) { + event.preventDefault(); + if ( ! isExpanded && item.parent ) { + const element = getFirstAncestor( event.currentTarget ); + return element?.focus(); + } + if ( item.children.length > 0 ) { + onCollapse(); + } + } + + if ( event.code === 'Enter' ) { + event.preventDefault(); + if ( item.children.length > 0 ) { + onToggleExpand(); + } + } + + if ( event.code === 'ArrowDown' || event.code === 'ArrowUp' ) { + event.preventDefault(); + const element = getNextFocusableElement( + event.currentTarget, + event.code + ); + element?.focus(); + if ( event.code === 'ArrowDown' && ! element && onLastItemLoop ) { + onLastItemLoop( event ); + } + } + + if ( event.code === 'Home' ) { + event.preventDefault(); + const element = getFirstFocusableElement( event.currentTarget ); + element?.focus(); + } + + if ( event.code === 'End' ) { + event.preventDefault(); + const element = getLastFocusableElement( event.currentTarget ); + element?.focus(); + } + } + + return { onKeyDown, onLastItemLoop }; +} diff --git a/packages/js/components/src/experimental-tree-control/hooks/use-linked-tree.ts b/packages/js/components/src/experimental-tree-control/hooks/use-linked-tree.ts new file mode 100644 index 00000000000..94ff95706b8 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/hooks/use-linked-tree.ts @@ -0,0 +1,50 @@ +/** + * External dependencies + */ +import { useMemo } from 'react'; + +/** + * Internal dependencies + */ +import { Item, LinkedTree } from '../types'; + +type MemoItems = { + [ value: Item[ 'value' ] ]: LinkedTree; +}; + +function findChildren( + items: Item[], + parent?: Item[ 'parent' ], + memo: MemoItems = {} +): LinkedTree[] { + const children: Item[] = []; + const others: Item[] = []; + + items.forEach( ( item ) => { + if ( item.parent === parent ) { + children.push( item ); + } else { + others.push( item ); + } + memo[ item.value ] = { + parent: undefined, + data: item, + children: [], + }; + } ); + + return children.map( ( child ) => { + const linkedTree = memo[ child.value ]; + linkedTree.parent = child.parent ? memo[ child.parent ] : undefined; + linkedTree.children = findChildren( others, child.value, memo ); + return linkedTree; + } ); +} + +export function useLinkedTree( items: Item[] ): LinkedTree[] { + const linkedTree = useMemo( () => { + return findChildren( items, undefined, {} ); + }, [ items ] ); + + return linkedTree; +} diff --git a/packages/js/components/src/experimental-tree-control/hooks/use-selection.ts b/packages/js/components/src/experimental-tree-control/hooks/use-selection.ts new file mode 100644 index 00000000000..6ad08a558a5 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/hooks/use-selection.ts @@ -0,0 +1,175 @@ +/** + * External dependencies + */ +import { useMemo } from 'react'; + +/** + * Internal dependencies + */ +import { CheckedStatus, Item, LinkedTree, TreeItemProps } from '../types'; + +let selectedItemsMap: Record< string, number > = {}; +let indeterminateMemo: Record< string, boolean > = {}; + +function getDeepChildren( item: LinkedTree ) { + if ( item.children.length ) { + const children = item.children.map( ( { data } ) => data ); + item.children.forEach( ( child ) => { + children.push( ...getDeepChildren( child ) ); + } ); + return children; + } + return []; +} + +function isIndeterminate( + selectedItems: Record< string, number >, + children?: LinkedTree[], + memo: Record< string, boolean > = indeterminateMemo +): boolean { + if ( children?.length ) { + for ( const child of children ) { + if ( child.data.value in indeterminateMemo ) { + return true; + } + const isChildSelected = child.data.value in selectedItems; + if ( + ! isChildSelected || + isIndeterminate( selectedItems, child.children, memo ) + ) { + indeterminateMemo[ child.data.value ] = true; + return true; + } + } + } + return false; +} + +function mapSelectedItems( + selected: Item | Item[] = [] +): Record< string, number > { + const selectedArray = Array.isArray( selected ) ? selected : [ selected ]; + return selectedArray.reduce( + ( map, selectedItem, index ) => ( { + ...map, + [ selectedItem.value ]: index, + } ), + {} as Record< string, number > + ); +} + +function hasSelectedSibblingChildren( + children: LinkedTree[], + values: Item[], + selectedItems: Record< string, number > +) { + return children.some( ( child ) => { + const isChildSelected = child.data.value in selectedItems; + if ( ! isChildSelected ) return false; + return ! values.some( + ( childValue ) => childValue.value === child.data.value + ); + } ); +} + +export function useSelection( { + item, + multiple, + shouldNotRecursivelySelect, + selected, + level, + index, + onSelect, + onRemove, +}: Pick< + TreeItemProps, + | 'item' + | 'multiple' + | 'selected' + | 'level' + | 'index' + | 'onSelect' + | 'onRemove' + | 'shouldNotRecursivelySelect' +> ) { + const selectedItems = useMemo( () => { + if ( level === 1 && index === 0 ) { + selectedItemsMap = mapSelectedItems( selected ); + indeterminateMemo = {} as Record< string, boolean >; + } + return selectedItemsMap; + }, [ selected, level, index ] ); + + const checkedStatus: CheckedStatus = useMemo( () => { + if ( item.data.value in selectedItems ) { + if ( + multiple && + ! shouldNotRecursivelySelect && + isIndeterminate( selectedItems, item.children ) + ) { + return 'indeterminate'; + } + return 'checked'; + } + return 'unchecked'; + }, [ selectedItems, item, multiple ] ); + + function onSelectChild( checked: boolean ) { + let value: Item | Item[] = item.data; + + if ( multiple ) { + value = [ item.data ]; + if ( item.children.length && ! shouldNotRecursivelySelect ) { + value.push( ...getDeepChildren( item ) ); + } + } + + if ( checked ) { + if ( typeof onSelect === 'function' ) { + onSelect( value ); + } + } else if ( typeof onRemove === 'function' ) { + onRemove( value ); + } + } + + function onSelectChildren( value: Item | Item[] ) { + if ( typeof onSelect !== 'function' ) return; + + if ( multiple && ! shouldNotRecursivelySelect ) { + value = [ item.data, ...( value as Item[] ) ]; + } + + onSelect( value ); + } + + function onRemoveChildren( value: Item | Item[] ) { + if ( typeof onRemove !== 'function' ) return; + + if ( + multiple && + item.children?.length && + ! shouldNotRecursivelySelect + ) { + const hasSelectedSibbling = hasSelectedSibblingChildren( + item.children, + value as Item[], + selectedItems + ); + if ( ! hasSelectedSibbling ) { + value = [ item.data, ...( value as Item[] ) ]; + } + } + + onRemove( value ); + } + + return { + multiple, + selected, + checkedStatus, + onSelectChild, + onSelectChildren, + onRemoveChildren, + }; +} diff --git a/packages/js/components/src/experimental-tree-control/hooks/use-tree-item.ts b/packages/js/components/src/experimental-tree-control/hooks/use-tree-item.ts new file mode 100644 index 00000000000..35a08d19042 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/hooks/use-tree-item.ts @@ -0,0 +1,112 @@ +/** + * External dependencies + */ +import React from 'react'; +import { useInstanceId } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import { TreeItemProps } from '../types'; +import { useExpander } from './use-expander'; +import { useHighlighter } from './use-highlighter'; +import { useKeyboard } from './use-keyboard'; +import { useSelection } from './use-selection'; + +export function useTreeItem( { + item, + level, + multiple, + shouldNotRecursivelySelect, + selected, + index, + getLabel, + shouldItemBeExpanded, + shouldItemBeHighlighted, + onSelect, + onRemove, + isExpanded, + onCreateNew, + shouldShowCreateButton, + onLastItemLoop, + onTreeBlur, + ...props +}: TreeItemProps ) { + const nextLevel = level + 1; + + const expander = useExpander( { + item, + shouldItemBeExpanded, + } ); + + const selection = useSelection( { + item, + multiple, + selected, + level, + index, + onSelect, + onRemove, + shouldNotRecursivelySelect, + } ); + + const highlighter = useHighlighter( { + item, + checkedStatus: selection.checkedStatus, + multiple, + shouldItemBeHighlighted, + } ); + + const subTreeId = `experimental-woocommerce-tree__group-${ useInstanceId( + useTreeItem + ) }`; + + const { onKeyDown } = useKeyboard( { + ...expander, + onLastItemLoop, + item, + } ); + + return { + item, + level: nextLevel, + expander, + selection, + highlighter, + getLabel, + treeItemProps: { + ...props, + role: 'none', + }, + headingProps: { + role: 'treeitem', + 'aria-selected': selection.checkedStatus !== 'unchecked', + 'aria-expanded': item.children.length + ? expander.isExpanded + : undefined, + 'aria-owns': + item.children.length && expander.isExpanded + ? subTreeId + : undefined, + style: { + '--level': level, + } as React.CSSProperties, + onKeyDown, + }, + treeProps: { + id: subTreeId, + items: item.children, + level: nextLevel, + multiple: selection.multiple, + selected: selection.selected, + role: 'group', + 'aria-label': item.data.label, + getItemLabel: getLabel, + shouldItemBeExpanded, + shouldItemBeHighlighted, + shouldNotRecursivelySelect, + onSelect: selection.onSelectChildren, + onRemove: selection.onRemoveChildren, + }, + }; +} diff --git a/packages/js/components/src/experimental-tree-control/hooks/use-tree.ts b/packages/js/components/src/experimental-tree-control/hooks/use-tree.ts new file mode 100644 index 00000000000..656473e9456 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/hooks/use-tree.ts @@ -0,0 +1,47 @@ +/** + * External dependencies + */ + +/** + * Internal dependencies + */ +import { TreeProps } from '../types'; + +export function useTree( { + items, + level = 1, + role = 'tree', + multiple, + selected, + getItemLabel, + shouldItemBeExpanded, + shouldItemBeHighlighted, + onSelect, + onRemove, + shouldNotRecursivelySelect, + createValue, + onTreeBlur, + onCreateNew, + shouldShowCreateButton, + ...props +}: TreeProps ) { + return { + level, + items, + treeProps: { + ...props, + role, + }, + treeItemProps: { + level, + multiple, + selected, + getLabel: getItemLabel, + shouldItemBeExpanded, + shouldItemBeHighlighted, + shouldNotRecursivelySelect, + onSelect, + onRemove, + }, + }; +} diff --git a/packages/js/components/src/experimental-tree-control/index.ts b/packages/js/components/src/experimental-tree-control/index.ts new file mode 100644 index 00000000000..bf4fdd7d970 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/index.ts @@ -0,0 +1,4 @@ +export * from './tree'; +export * from './tree-control'; +export * from './tree-item'; +export * from './types'; diff --git a/packages/js/components/src/experimental-tree-control/stories/index.tsx b/packages/js/components/src/experimental-tree-control/stories/index.tsx new file mode 100644 index 00000000000..2ddcffd2c67 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/stories/index.tsx @@ -0,0 +1,260 @@ +/** + * External dependencies + */ +import interpolate from '@automattic/interpolate-components'; +import { BaseControl, TextControl } from '@wordpress/components'; +import React, { createElement, useCallback, useRef, useState } from 'react'; + +/** + * Internal dependencies + */ +import { TreeControl } from '../tree-control'; +import { Item, LinkedTree } from '../types'; +import '../tree.scss'; + +const listItems: Item[] = [ + { value: '1', label: 'Technology' }, + { 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' }, +]; + +export const SimpleTree: React.FC = () => { + return ( + + + + ); +}; + +function shouldItemBeExpanded( item: LinkedTree, filter: string ) { + if ( ! filter || ! item.children?.length ) return false; + return item.children.some( ( child ) => { + if ( new RegExp( filter, 'ig' ).test( child.data.label ) ) { + return true; + } + return shouldItemBeExpanded( child, filter ); + } ); +} + +export const ExpandOnFilter: React.FC = () => { + const [ filter, setFilter ] = useState( '' ); + + return ( + <> + + + + shouldItemBeExpanded( item, filter ) + } + /> + + + ); +}; + +export const CustomItemLabel: React.FC = () => { + function renderCustomItemLabel( item: LinkedTree ) { + return ( +
    +
    +
    + { item.data.label } + Some item description +
    +
    + ); + } + + return ( + + + + ); +}; + +function getItemLabel( item: LinkedTree, text: string ) { + return ( + + { text + ? interpolate( { + mixedString: item.data.label.replace( + new RegExp( text, 'ig' ), + ( group ) => `{{bold}}${ group }{{/bold}}` + ), + components: { + bold: , + }, + } ) + : item.data.label } + + ); +} + +export const CustomItemLabelOnSearch: React.FC = () => { + const [ text, setText ] = useState( '' ); + + return ( + <> + + + getItemLabel( item, text ) } + shouldItemBeExpanded={ useCallback( + ( item ) => shouldItemBeExpanded( item, text ), + [ text ] + ) } + /> + + + ); +}; + +export const SelectionSingle: React.FC = () => { + const [ selected, setSelected ] = useState( listItems[ 1 ] ); + + return ( + <> + + setSelected( value ) } + /> + + +
    { JSON.stringify( selected, null, 2 ) }
    + + ); +}; + +export const SelectionMultiple: React.FC = () => { + const [ selected, setSelected ] = useState( [ + listItems[ 0 ], + listItems[ 1 ], + ] ); + + function handleSelect( values: Item[] ) { + setSelected( ( items ) => { + const newItems = values.filter( + ( { value } ) => + ! items.some( ( item ) => item.value === value ) + ); + return [ ...items, ...newItems ]; + } ); + } + + function handleRemove( values: Item[] ) { + setSelected( ( items ) => + items.filter( + ( item ) => + ! values.some( ( { value } ) => item.value === value ) + ) + ); + } + + return ( + <> + + + + +
    { JSON.stringify( selected, null, 2 ) }
    + + ); +}; + +function getFirstMatchingItem( + item: LinkedTree, + text: string, + memo: Record< string, string > +) { + if ( ! text ) return false; + if ( memo[ text ] === item.data.value ) return true; + + const matcher = new RegExp( text, 'ig' ); + if ( matcher.test( item.data.label ) ) { + if ( ! memo[ text ] ) { + memo[ text ] = item.data.value; + return true; + } + } + + return false; +} + +export const HighlightFirstMatchingItem: React.FC = () => { + const [ text, setText ] = useState( '' ); + const memo = useRef< Record< string, string > >( {} ); + + return ( + <> + + + getItemLabel( item, text ) } + shouldItemBeExpanded={ useCallback( + ( item ) => shouldItemBeExpanded( item, text ), + [ text ] + ) } + shouldItemBeHighlighted={ ( item ) => + getFirstMatchingItem( item, text, memo.current ) + } + /> + + + ); +}; + +export default { + title: 'WooCommerce Admin/experimental/TreeControl', + component: TreeControl, +}; diff --git a/packages/js/components/src/experimental-tree-control/tree-control.tsx b/packages/js/components/src/experimental-tree-control/tree-control.tsx new file mode 100644 index 00000000000..24a484a2995 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/tree-control.tsx @@ -0,0 +1,20 @@ +/** + * External dependencies + */ +import { createElement, forwardRef } from 'react'; + +/** + * Internal dependencies + */ +import { useLinkedTree } from './hooks/use-linked-tree'; +import { Tree } from './tree'; +import { TreeControlProps } from './types'; + +export const TreeControl = forwardRef( function ForwardedTree( + { items, ...props }: TreeControlProps, + ref: React.ForwardedRef< HTMLOListElement > +) { + const linkedTree = useLinkedTree( items ); + + return ; +} ); diff --git a/packages/js/components/src/experimental-tree-control/tree-item.scss b/packages/js/components/src/experimental-tree-control/tree-item.scss new file mode 100644 index 00000000000..4f849e68fd1 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/tree-item.scss @@ -0,0 +1,89 @@ +$control-size: $gap-large; + +.experimental-woocommerce-tree-item { + margin: 0; + + &--highlighted { + > .experimental-woocommerce-tree-item__heading { + background-color: $gray-100; + } + } + + &__heading { + display: flex; + flex-grow: 1; + gap: $gap-smaller; + min-height: $gap-largest; + padding: 0 $gap-small 0 + calc( ( var( --level ) - 1 ) * ( $gap + $gap-small ) + $gap-small ); + border-radius: 2px; + + &:hover, + &:focus-within { + outline: 1.5px solid var( --wp-admin-theme-color ); + outline-offset: -1.5px; + } + + &:hover, + &:focus-within { + background-color: $gray-100; + } + } + + &__label { + display: flex; + flex-grow: 1; + align-items: center; + padding: $gap-smaller $gap-small $gap-smaller 0; + position: relative; + + > span { + display: block; + } + + .components-base-control__field { + margin: 0; + } + + .components-checkbox-control__label { + display: none; + } + + .components-checkbox-control__input-container { + display: block; + width: $control-size; + height: $control-size; + } + + svg.components-checkbox-control__checked, + svg.components-checkbox-control__indeterminate, + .components-checkbox-control__input[type='checkbox'] { + position: absolute; + border-color: $gray-700; + width: $control-size; + height: $control-size; + top: 0; + left: 0; + &:focus { + outline: none; + box-shadow: none; + } + } + } + + &__expander { + display: flex; + align-items: center; + + .components-button { + padding: 0; + height: $control-size; + width: $control-size; + min-width: $control-size; + } + } + + &__checkbox { + @include screen-reader-only(); + } +} diff --git a/packages/js/components/src/experimental-tree-control/tree-item.tsx b/packages/js/components/src/experimental-tree-control/tree-item.tsx new file mode 100644 index 00000000000..6f87e716b90 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/tree-item.tsx @@ -0,0 +1,100 @@ +/** + * External dependencies + */ +import { Button, CheckboxControl } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { chevronDown, chevronUp } from '@wordpress/icons'; +import classNames from 'classnames'; +import { createElement, forwardRef } from 'react'; + +/** + * Internal dependencies + */ +import { useTreeItem } from './hooks/use-tree-item'; +import { Tree } from './tree'; +import { TreeItemProps } from './types'; + +export const TreeItem = forwardRef( function ForwardedTreeItem( + props: TreeItemProps, + ref: React.ForwardedRef< HTMLLIElement > +) { + const { + item, + treeItemProps, + headingProps, + treeProps, + expander: { isExpanded, onToggleExpand }, + selection, + highlighter: { isHighlighted }, + getLabel, + } = useTreeItem( { + ...props, + ref, + } ); + + return ( +
  • +
    + { /* eslint-disable-next-line jsx-a11y/label-has-for */ } + + + { Boolean( item.children?.length ) && ( +
    +
    + ) } +
    + + { Boolean( item.children.length ) && isExpanded && ( + + ) } +
  • + ); +} ); diff --git a/packages/js/components/src/experimental-tree-control/tree.scss b/packages/js/components/src/experimental-tree-control/tree.scss new file mode 100644 index 00000000000..f33dfe422a2 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/tree.scss @@ -0,0 +1,24 @@ +@import './tree-item.scss'; + +.experimental-woocommerce-tree { + list-style: none; + padding: 0; + margin: 0; + + &--level-1 { + max-height: 280px; + overflow-y: auto; + background-color: $white; + 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; + } + } +} diff --git a/packages/js/components/src/experimental-tree-control/tree.tsx b/packages/js/components/src/experimental-tree-control/tree.tsx new file mode 100644 index 00000000000..491c6c161bd --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/tree.tsx @@ -0,0 +1,109 @@ +/** + * External dependencies + */ +import { Button, Icon } from '@wordpress/components'; +import { __, sprintf } from '@wordpress/i18n'; +import classNames from 'classnames'; +import { createElement, forwardRef, Fragment, useRef } from 'react'; +import { plus } from '@wordpress/icons'; +import { useMergeRefs } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import { useTree } from './hooks/use-tree'; +import { TreeItem } from './tree-item'; +import { TreeProps } from './types'; + +export const Tree = forwardRef( function ForwardedTree( + props: TreeProps, + forwardedRef: React.ForwardedRef< HTMLOListElement > +) { + const rootListRef = useRef< HTMLOListElement >( null ); + const ref = useMergeRefs( [ rootListRef, forwardedRef ] ); + + const { level, items, treeProps, treeItemProps } = useTree( { + ...props, + ref, + } ); + + const isCreateButtonVisible = + props.shouldShowCreateButton && + props.shouldShowCreateButton( props.createValue ); + + return ( + <> +
      + { items.map( ( child, index ) => ( + { + ( + rootListRef.current + ?.closest( 'ol[role="tree"]' ) + ?.parentElement?.querySelector( + '.experimental-woocommerce-tree__button' + ) as HTMLButtonElement + )?.focus(); + } } + /> + ) ) } +
    + { isCreateButtonVisible && ( + + ) } + + ); +} ); diff --git a/packages/js/components/src/experimental-tree-control/types.ts b/packages/js/components/src/experimental-tree-control/types.ts new file mode 100644 index 00000000000..8c60fd36194 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/types.ts @@ -0,0 +1,141 @@ +export interface Item { + parent?: string; + value: string; + label: string; +} + +export interface LinkedTree { + parent?: LinkedTree; + data: Item; + children: LinkedTree[]; +} + +export type CheckedStatus = 'checked' | 'unchecked' | 'indeterminate'; + +type BaseTreeProps = { + /** + * It contains one item if `multiple` value is false or + * a list of items if it is true. + */ + selected?: Item | Item[]; + /** + * Whether the tree items are single or multiple selected. + */ + multiple?: boolean; + /** + * 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 + */ + onSelect?( value: Item | Item[] ): void; + /** + * When `multiple` is true and a child item is unselected, all its + * ancestors (if no sibblings are selected) and its descendants + * are also unselected. If it's false only the clicked item is + * unselected. + * + * @param value The unselection + */ + onRemove?( value: Item | Item[] ): void; + /** + * It provides a way to determine whether the current rendering + * item is highlighted or not from outside the tree. + * + * @example + * + * + * @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 & + Omit< + React.DetailedHTMLProps< + React.OlHTMLAttributes< HTMLOListElement >, + HTMLOListElement + >, + 'onSelect' + > & { + level?: number; + items: LinkedTree[]; + /** + * It gives a way to render a different Element as the + * tree item label. + * + * @example + * ${ item.data.label } } + * /> + * + * @param item The current rendering tree item + * + * @see {@link LinkedTree} + */ + getItemLabel?( item: LinkedTree ): JSX.Element; + /** + * Return if the tree item passed in should be expanded. + * + * @example + * checkExpanded( item, filter ) + * } + * /> + * + * @param item The tree item to determine if should be expanded. + * + * @see {@link LinkedTree} + */ + shouldItemBeExpanded?( item: LinkedTree ): boolean; + }; + +export type TreeItemProps = BaseTreeProps & + Omit< + React.DetailedHTMLProps< + React.LiHTMLAttributes< HTMLLIElement >, + HTMLLIElement + >, + 'onSelect' + > & { + level: number; + item: LinkedTree; + index: number; + isFocused?: boolean; + getLabel?( item: LinkedTree ): JSX.Element; + shouldItemBeExpanded?( item: LinkedTree ): boolean; + onLastItemLoop?( event: React.KeyboardEvent< HTMLDivElement > ): void; + }; + +export type TreeControlProps = Omit< TreeProps, 'items' | 'level' > & { + items: Item[]; +}; diff --git a/packages/js/components/src/experimental-tree-control/typings/global.d.ts b/packages/js/components/src/experimental-tree-control/typings/global.d.ts new file mode 100644 index 00000000000..ba0ce7abdf0 --- /dev/null +++ b/packages/js/components/src/experimental-tree-control/typings/global.d.ts @@ -0,0 +1,9 @@ +import * as components from '@wordpress/components'; + +declare module '@wordpress/components' { + declare namespace CheckboxControl { + interface Props { + indeterminate?: boolean; + } + } +} diff --git a/packages/js/components/src/filters/index.js b/packages/js/components/src/filters/index.js index a6051655d54..ec82939bd46 100644 --- a/packages/js/components/src/filters/index.js +++ b/packages/js/components/src/filters/index.js @@ -7,7 +7,7 @@ import { find } from 'lodash'; import PropTypes from 'prop-types'; import { updateQueryString } from '@woocommerce/navigation'; import { getDateParamsFromQuery, getCurrentDates } from '@woocommerce/date'; -import CurrencyFactory from '@woocommerce/currency'; +import { CurrencyFactory } from '@woocommerce/currency'; /** * Internal dependencies diff --git a/packages/js/components/src/filters/stories/index.js b/packages/js/components/src/filters/stories/index.js index 30d5fc9d021..3683c253758 100644 --- a/packages/js/components/src/filters/stories/index.js +++ b/packages/js/components/src/filters/stories/index.js @@ -25,6 +25,16 @@ const ORDER_STATUSES = { refunded: 'Refunded', }; +const CURRENCY = { + code: 'USD', + decimalSeparator: '.', + precision: 2, + priceFormat: '%1$s%2$s', + symbol: '$', + symbolPosition: 'left', + thousandSeparator: ',', +}; + // Fetch store default date range and compose with date utility functions. const defaultDateRange = 'period=month&compare=previous_year'; const storeGetDateParamsFromQuery = partialRight( @@ -58,14 +68,14 @@ const filters = [ ]; const advancedFilters = { - title: 'Orders Match {{select /}} Filters', + title: 'Orders Match + { fieldConfigs.map( ( field ) => ( + + ) ) } + + { selectedFieldConfig && + renderField( selectedFieldConfig.type, { + value, + onChange: setValue, + ...selectedFieldConfig, + } ) } +
    + ); +}; + +export const Basic: React.FC = () => { + return ( + + + + ); +}; + +export const ToggleWithTooltip: React.FC = () => { + const [ value, setValue ] = useState(); + return ( + + { renderField( 'toggle', { + value, + onChange: setValue, + name: 'toggle', + label: 'Toggle with Tooltip', + tooltip: 'This is a sample tooltip', + } ) } + + ); +}; + +export default { + title: 'WooCommerce Admin/experimental/product-fields', + component: Basic, +}; diff --git a/packages/js/components/src/product-image/index.js b/packages/js/components/src/product-image/index.js deleted file mode 100644 index 799b61eb2aa..00000000000 --- a/packages/js/components/src/product-image/index.js +++ /dev/null @@ -1,89 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; -import PropTypes from 'prop-types'; -import { get } from 'lodash'; -import { createElement } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { placeholderWhiteBackground as placeholder } from './placeholder'; - -/** - * Use `ProductImage` to display a product's or variation's featured image. - * If no image can be found, a placeholder matching the front-end image - * placeholder will be displayed. - * - * @param {Object} props - * @param {Object} props.product - * @param {string} props.alt - * @param {number} props.width - * @param {number} props.height - * @param {string} props.className - * @return {Object} - - */ -const ProductImage = ( { - product, - alt, - width, - height, - className, - ...props -} ) => { - // The first returned image from the API is the featured/product image. - const productImage = - get( product, [ 'images', 0 ] ) || get( product, [ 'image' ] ); - const src = ( productImage && productImage.src ) || false; - const altText = alt || ( productImage && productImage.alt ) || ''; - - const classes = classnames( 'woocommerce-product-image', className, { - 'is-placeholder': ! src, - } ); - - return ( - { - ); -}; - -ProductImage.propTypes = { - /** - * The width of image to display. - */ - width: PropTypes.number, - /** - * The height of image to display. - */ - height: PropTypes.number, - /** - * Additional CSS classes. - */ - className: PropTypes.string, - /** - * Product or variation object. The image to display will be pulled from - * `product.images` or `variation.image`. - * See https://woocommerce.github.io/woocommerce-rest-api-docs/#product-properties - * and https://woocommerce.github.io/woocommerce-rest-api-docs/#product-variation-properties - */ - product: PropTypes.object, - /** - * Text to use as the image alt attribute. - */ - alt: PropTypes.string, -}; - -ProductImage.defaultProps = { - width: 33, - height: 33, - className: '', -}; - -export default ProductImage; diff --git a/packages/js/components/src/product-image/index.tsx b/packages/js/components/src/product-image/index.tsx new file mode 100644 index 00000000000..a6b942648aa --- /dev/null +++ b/packages/js/components/src/product-image/index.tsx @@ -0,0 +1,78 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import { get } from 'lodash'; +import { createElement } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { placeholderWhiteBackground as placeholder } from './placeholder'; + +type Image = { + src?: string; +}; + +type ProductImageProps = { + /** + * Product or variation object. The image to display will be pulled from + * `product.images` or `variation.image`. + * See https://woocommerce.github.io/woocommerce-rest-api-docs/#product-properties + * and https://woocommerce.github.io/woocommerce-rest-api-docs/#product-variation-properties + */ + product?: { + images?: Array< Image >; + image?: Image; + // ProductImage is only interested in product.images or varation.image + // but product object can have other properties that we don't control. + // allowing `any` here + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } & Record< string, any >; + /** The width of image to display. */ + width?: number; + /** The height of image to display. */ + height?: number; + /** Additional CSS classes. */ + className?: string; + /** Text to use as the image alt attribute. */ + alt?: string; +}; + +/** + * Use `ProductImage` to display a product's or variation's featured image. + * If no image can be found, a placeholder matching the front-end image + * placeholder will be displayed. + */ + +const ProductImage: React.VFC< ProductImageProps > = ( { + product, + width = 33, + height = 33, + className = '', + alt, + ...props +} ) => { + // The first returned image from the API is the featured/product image. + const productImage = + get( product, [ 'images', 0 ] ) || get( product, [ 'image' ] ); + const src = ( productImage && productImage.src ) || false; + const altText = alt || ( productImage && productImage.alt ) || ''; + + const classes = classnames( 'woocommerce-product-image', className, { + 'is-placeholder': ! src, + } ); + + return ( + { + ); +}; + +export default ProductImage; diff --git a/packages/js/components/src/product-image/placeholder.js b/packages/js/components/src/product-image/placeholder.tsx similarity index 100% rename from packages/js/components/src/product-image/placeholder.js rename to packages/js/components/src/product-image/placeholder.tsx diff --git a/packages/js/components/src/product-image/stories/index.js b/packages/js/components/src/product-image/stories/index.js deleted file mode 100644 index 1d6f0a504a6..00000000000 --- a/packages/js/components/src/product-image/stories/index.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * External dependencies - */ -import { ProductImage } from '@woocommerce/components'; - -export const Basic = () => ( -
    - - - -
    -); - -export default { - title: 'WooCommerce Admin/components/ProductImage', - component: ProductImage, -}; diff --git a/packages/js/components/src/product-image/stories/index.tsx b/packages/js/components/src/product-image/stories/index.tsx new file mode 100644 index 00000000000..12bc3473226 --- /dev/null +++ b/packages/js/components/src/product-image/stories/index.tsx @@ -0,0 +1,26 @@ +/** + * External dependencies + */ +import { ProductImage } from '@woocommerce/components'; +import { createElement } from '@wordpress/element'; + +export const Basic = () => ( +
    + + + +
    +); + +export default { + title: 'WooCommerce Admin/components/ProductImage', + component: ProductImage, +}; diff --git a/packages/js/components/src/product-image/test/__snapshots__/index.js.snap b/packages/js/components/src/product-image/test/__snapshots__/index.tsx.snap similarity index 100% rename from packages/js/components/src/product-image/test/__snapshots__/index.js.snap rename to packages/js/components/src/product-image/test/__snapshots__/index.tsx.snap diff --git a/packages/js/components/src/product-image/test/index.js b/packages/js/components/src/product-image/test/index.js deleted file mode 100644 index f22c107d7e0..00000000000 --- a/packages/js/components/src/product-image/test/index.js +++ /dev/null @@ -1,82 +0,0 @@ -/** - * External dependencies - */ -import { render } from '@testing-library/react'; -import { createElement } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import ProductImage from '../'; - -describe( 'ProductImage', () => { - test( 'should render the passed alt prop', () => { - const { container } = render( ); - expect( container ).toMatchSnapshot(); - } ); - - test( 'should fallback to product alt text', () => { - const product = { - name: 'Test Product', - images: [ - { - src: 'https://i.cloudup.com/pt4DjwRB84-3000x3000.png', - alt: 'hello world', - }, - ], - }; - const { container } = render( ); - expect( container ).toMatchSnapshot(); - } ); - - test( 'should fallback to empty alt attribute if not passed via prop or product object', () => { - const product = { - name: 'Test Product', - images: [ - { - src: 'https://i.cloudup.com/pt4DjwRB84-3000x3000.png', - }, - ], - }; - const { container } = render( ); - expect( container ).toMatchSnapshot(); - } ); - - test( 'should have the correct width and height', () => { - const image = ; - expect( image.props.width ).toBe( 30 ); - expect( image.props.height ).toBe( 30 ); - } ); - - test( 'should render a product image', () => { - const product = { - name: 'Test Product', - images: [ - { - src: 'https://i.cloudup.com/pt4DjwRB84-3000x3000.png', - }, - ], - }; - const { container } = render( ); - expect( container ).toMatchSnapshot(); - } ); - - test( 'should render a variations image', () => { - const variation = { - name: 'Test Variation', - image: { - src: 'https://i.cloudup.com/pt4DjwRB84-3000x3000.png', - }, - }; - const { container } = render( ); - expect( container ).toMatchSnapshot(); - } ); - - test( 'should render a placeholder image if no product images are found', () => { - const product = { - name: 'Test Product', - }; - const { container } = render( ); - expect( container ).toMatchSnapshot(); - } ); -} ); diff --git a/packages/js/components/src/product-image/test/index.tsx b/packages/js/components/src/product-image/test/index.tsx new file mode 100644 index 00000000000..52995367574 --- /dev/null +++ b/packages/js/components/src/product-image/test/index.tsx @@ -0,0 +1,82 @@ +/** + * External dependencies + */ +import { render } from '@testing-library/react'; +import { createElement } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import ProductImage from '..'; + +describe( 'ProductImage', () => { + test( 'should render the passed alt prop', () => { + const { container } = render( ); + expect( container ).toMatchSnapshot(); + } ); + + test( 'should fallback to product alt text', () => { + const product = { + name: 'Test Product', + images: [ + { + src: 'https://i.cloudup.com/pt4DjwRB84-3000x3000.png', + alt: 'hello world', + }, + ], + }; + const { container } = render( ); + expect( container ).toMatchSnapshot(); + } ); + + test( 'should fallback to empty alt attribute if not passed via prop or product object', () => { + const product = { + name: 'Test Product', + images: [ + { + src: 'https://i.cloudup.com/pt4DjwRB84-3000x3000.png', + }, + ], + }; + const { container } = render( ); + expect( container ).toMatchSnapshot(); + } ); + + test( 'should have the correct width and height', () => { + const image = ; + expect( image.props.width ).toBe( 30 ); + expect( image.props.height ).toBe( 30 ); + } ); + + test( 'should render a product image', () => { + const product = { + name: 'Test Product', + images: [ + { + src: 'https://i.cloudup.com/pt4DjwRB84-3000x3000.png', + }, + ], + }; + const { container } = render( ); + expect( container ).toMatchSnapshot(); + } ); + + test( 'should render a variations image', () => { + const variation = { + name: 'Test Variation', + image: { + src: 'https://i.cloudup.com/pt4DjwRB84-3000x3000.png', + }, + }; + const { container } = render( ); + expect( container ).toMatchSnapshot(); + } ); + + test( 'should render a placeholder image if no product images are found', () => { + const product = { + name: 'Test Product', + }; + const { container } = render( ); + expect( container ).toMatchSnapshot(); + } ); +} ); diff --git a/packages/js/components/src/product-section-layout/index.ts b/packages/js/components/src/product-section-layout/index.ts new file mode 100644 index 00000000000..9c670115867 --- /dev/null +++ b/packages/js/components/src/product-section-layout/index.ts @@ -0,0 +1,2 @@ +export * from './product-section-layout'; +export * from './product-field-section'; diff --git a/packages/js/components/src/product-section-layout/product-field-section.tsx b/packages/js/components/src/product-section-layout/product-field-section.tsx new file mode 100644 index 00000000000..d06d03669b9 --- /dev/null +++ b/packages/js/components/src/product-section-layout/product-field-section.tsx @@ -0,0 +1,47 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { Card, CardBody } from '@wordpress/components'; +import deprecated from '@wordpress/deprecated'; + +/** + * Internal dependencies + */ +import { ProductSectionLayout } from './product-section-layout'; +import { WooProductFieldItem } from '../woo-product-field-item'; + +type ProductFieldSectionProps = { + id: string; + title: string; + description: string | JSX.Element; + className?: string; +}; + +export const ProductFieldSection: React.FC< ProductFieldSectionProps > = ( { + id, + title, + description, + className, + children, +} ) => { + deprecated( `__experimentalProductFieldSection`, { + version: '13.0.0', + plugin: '@woocommerce/components', + hint: 'Moved to @woocommerce/product-editor package: import { __experimentalProductFieldSection } from @woocommerce/product-editor', + } ); + return ( + + + + { children } + + + + + ); +}; diff --git a/packages/js/components/src/product-section-layout/product-section-layout.tsx b/packages/js/components/src/product-section-layout/product-section-layout.tsx new file mode 100644 index 00000000000..66c8c84cb68 --- /dev/null +++ b/packages/js/components/src/product-section-layout/product-section-layout.tsx @@ -0,0 +1,44 @@ +/** + * External dependencies + */ +import { Children, isValidElement, createElement } from '@wordpress/element'; +import deprecated from '@wordpress/deprecated'; +/** + * Internal dependencies + */ +import { FormSection } from '../form-section'; + +type ProductSectionLayoutProps = { + title: string; + description: string | JSX.Element; + className?: string; +}; + +export const ProductSectionLayout: React.FC< ProductSectionLayoutProps > = ( { + title, + description, + className, + children, +} ) => { + deprecated( `__experimentalProductSectionLayout`, { + version: '13.0.0', + plugin: '@woocommerce/components', + hint: 'Moved to @woocommerce/product-editor package: import { __experimentalProductSectionLayout } from @woocommerce/product-editor', + } ); + return ( + + { Children.map( children, ( child ) => { + if ( isValidElement( child ) && child.props.onChange ) { + return ( +
    { child }
    + ); + } + return child; + } ) } +
    + ); +}; diff --git a/packages/js/components/src/product-section-layout/style.scss b/packages/js/components/src/product-section-layout/style.scss new file mode 100644 index 00000000000..64753f99ecd --- /dev/null +++ b/packages/js/components/src/product-section-layout/style.scss @@ -0,0 +1,52 @@ +.woocommerce-form-section { + a { + text-decoration: none; + } + + &__content { + .components-card { + border: 1px solid $gray-400; + border-radius: 2px; + box-shadow: none; + &__body { + padding: $gap-large; + + .components-base-control, + .components-dropdown, + .woocommerce-rich-text-editor { + &:not(:first-child):not(.components-radio-control) { + margin-top: $gap-large - $gap-smaller; + margin-bottom: 0; + } + } + } + } + + .woocommerce-product-form__field:not(:first-child) { + margin-top: $gap-large; + + > .components-base-control { + margin-bottom: 0; + } + } + + .components-radio-control .components-v-stack { + gap: $gap-small; + } + + .woocommerce-collapsible-content { + margin-top: $gap-large; + } + } + + &__header { + p > span { + display: block; + margin-bottom: $gap-smaller; + } + } + + &:not(:first-child) { + margin-top: $gap-largest; + } +} diff --git a/packages/js/components/src/rating/index.js b/packages/js/components/src/rating/index.js deleted file mode 100644 index 8e36d15ca4a..00000000000 --- a/packages/js/components/src/rating/index.js +++ /dev/null @@ -1,92 +0,0 @@ -/** - * External dependencies - */ -import { __, sprintf } from '@wordpress/i18n'; -import classnames from 'classnames'; -import { createElement, Component } from '@wordpress/element'; -import StarIcon from 'gridicons/dist/star'; -import PropTypes from 'prop-types'; - -/** - * Use `Rating` to display a set of stars, filled, empty or half-filled, that represents a - * rating in a scale between 0 and the prop `totalStars` (default 5). - */ -class Rating extends Component { - stars( icon ) { - const { size, totalStars } = this.props; - - const starStyles = { - width: size + 'px', - height: size + 'px', - }; - - const stars = []; - for ( let i = 0; i < totalStars; i++ ) { - const Icon = icon || StarIcon; - stars.push( ); - } - return stars; - } - - render() { - const { rating, totalStars, className, icon, outlineIcon } = this.props; - - const classes = classnames( 'woocommerce-rating', className ); - const perStar = 100 / totalStars; - const outlineStyles = { - width: Math.round( perStar * rating ) + '%', - }; - - const label = sprintf( - __( '%1$s out of %2$s stars.', 'woocommerce' ), - rating, - totalStars - ); - return ( -
    - { this.stars( icon ) } -
    - { this.stars( outlineIcon || icon ) } -
    -
    - ); - } -} - -Rating.propTypes = { - /** - * Number of stars that should be filled. You can pass a partial number of stars like `2.5`. - */ - rating: PropTypes.number, - /** - * The total number of stars the rating is out of. - */ - totalStars: PropTypes.number, - /** - * The size in pixels the stars should be rendered at. - */ - size: PropTypes.number, - /** - * Additional CSS classes. - */ - className: PropTypes.string, - /** - * Icon used, defaults to StarIcon - */ - icon: PropTypes.elementType, - /** - * Outline icon used, the not selected rating. Defaults to props.icon or StarIcon - */ - outlineIcon: PropTypes.elementType, -}; - -Rating.defaultProps = { - rating: 0, - totalStars: 5, - size: 18, -}; - -export default Rating; diff --git a/packages/js/components/src/rating/index.tsx b/packages/js/components/src/rating/index.tsx new file mode 100644 index 00000000000..bb598cc40ce --- /dev/null +++ b/packages/js/components/src/rating/index.tsx @@ -0,0 +1,74 @@ +/** + * External dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import classnames from 'classnames'; +import { createElement } from '@wordpress/element'; +import StarIcon from 'gridicons/dist/star'; + +type RatingProps = { + // Number of stars that should be filled. You can pass a partial number of stars like `2.5`. + rating?: number; + // The total number of stars the rating is out of. + totalStars?: number; + // The size in pixels the stars should be rendered at. + size?: number; + // Additional CSS classes. + className?: string; + // Icon used, defaults to StarIcon + icon?: React.ReactNode; + // Outline icon used, the not selected rating. Defaults to props.icon or StarIcon + outlineIcon?: React.ReactNode; +}; + +/** + * Use `Rating` to display a set of stars, filled, empty or half-filled, that represents a + * rating in a scale between 0 and the prop `totalStars` (default 5). + */ +const Rating = ( { + rating = 0, + totalStars = 5, + size = 18, + className, + icon, + outlineIcon, +}: RatingProps ) => { + const stars = ( _icon: React.ReactNode ) => { + const starStyles = { + width: size + 'px', + height: size + 'px', + }; + + const _stars = []; + for ( let i = 0; i < totalStars; i++ ) { + const Icon = _icon || StarIcon; + _stars.push( ); + } + return _stars; + }; + + const classes = classnames( 'woocommerce-rating', className ); + const perStar = 100 / totalStars; + const outlineStyles = { + width: Math.round( perStar * rating ) + '%', + }; + + const label = sprintf( + __( '%1$s out of %2$s stars.', 'woocommerce' ), + rating, + totalStars + ); + return ( +
    + { stars( icon ) } +
    + { stars( outlineIcon || icon ) } +
    +
    + ); +}; + +export default Rating; diff --git a/packages/js/components/src/rating/product.js b/packages/js/components/src/rating/product.js deleted file mode 100644 index ea5a8b4131d..00000000000 --- a/packages/js/components/src/rating/product.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * External dependencies - */ -import PropTypes from 'prop-types'; -import { createElement } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import Rating from './index'; - -/** - * Display a set of stars representing the product's average rating. - * - * @param {Object} props - * @param {Object} props.product - * @return {Object} - - */ -const ProductRating = ( { product, ...props } ) => { - const rating = ( product && product.average_rating ) || 0; - return ; -}; - -ProductRating.propTypes = { - /** - * A product object containing a `average_rating`. - * See https://woocommerce.github.io/woocommerce-rest-api-docs/#products. - */ - product: PropTypes.object.isRequired, -}; - -export default ProductRating; diff --git a/packages/js/components/src/rating/product.tsx b/packages/js/components/src/rating/product.tsx new file mode 100644 index 00000000000..f76f17e04ea --- /dev/null +++ b/packages/js/components/src/rating/product.tsx @@ -0,0 +1,36 @@ +/** + * External dependencies + */ +import PropTypes from 'prop-types'; +import { createElement } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import Rating from './index'; + +type ProductRatingProps = { + product: { + average_rating?: number; + }; +}; +/** + * Display a set of stars representing the product's average rating. + */ +const ProductRating: React.VFC< ProductRatingProps > = ( { + product, + ...props +} ) => { + const rating = ( product && product.average_rating ) || 0; + return ; +}; + +ProductRating.propTypes = { + /** + * A product object containing a `average_rating`. + * See https://woocommerce.github.io/woocommerce-rest-api-docs/#products. + */ + product: PropTypes.object.isRequired, +}; + +export default ProductRating; diff --git a/packages/js/components/src/rating/review.js b/packages/js/components/src/rating/review.js deleted file mode 100644 index 5e3cb468298..00000000000 --- a/packages/js/components/src/rating/review.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * External dependencies - */ -import PropTypes from 'prop-types'; -import { createElement } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import Rating from './index'; - -/** - * Display a set of stars representing the review's rating. - * - * @param {Object} props - * @param {Object} props.review - * @return {Object} - - */ -const ReviewRating = ( { review, ...props } ) => { - const rating = ( review && review.rating ) || 0; - return ; -}; - -ReviewRating.propTypes = { - /** - * A review object containing a `rating`. - * See https://woocommerce.github.io/woocommerce-rest-api-docs/#retrieve-product-reviews. - */ - review: PropTypes.object.isRequired, -}; - -export default ReviewRating; diff --git a/packages/js/components/src/rating/review.tsx b/packages/js/components/src/rating/review.tsx new file mode 100644 index 00000000000..1cba308675f --- /dev/null +++ b/packages/js/components/src/rating/review.tsx @@ -0,0 +1,36 @@ +/** + * External dependencies + */ +import PropTypes from 'prop-types'; +import { createElement } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import Rating from './index'; + +type ReviewRatingProps = { + review: { + rating?: number; + }; +}; + +/** + * Display a set of stars representing the review's rating. + */ +const ReviewRating: React.VFC< ReviewRatingProps > = ( { + review, + ...props +} ) => { + return ; +}; + +ReviewRating.propTypes = { + /** + * A review object containing a `rating`. + * See https://woocommerce.github.io/woocommerce-rest-api-docs/#retrieve-product-reviews. + */ + review: PropTypes.object.isRequired, +}; + +export default ReviewRating; diff --git a/packages/js/components/src/rating/stories/index.js b/packages/js/components/src/rating/stories/index.js deleted file mode 100644 index c3d57d32e0e..00000000000 --- a/packages/js/components/src/rating/stories/index.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Internal dependencies - */ -import Rating from '../'; - -export default { - title: 'WooCommerce Admin/components/Rating', - component: Rating, - args: { - rating: 4.5, - totalStars: Rating.defaultProps.totalStars, - size: Rating.defaultProps.size, - }, -}; - -export const Default = ( args ) => ; diff --git a/packages/js/components/src/rating/stories/index.tsx b/packages/js/components/src/rating/stories/index.tsx new file mode 100644 index 00000000000..36acd7b8707 --- /dev/null +++ b/packages/js/components/src/rating/stories/index.tsx @@ -0,0 +1,21 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import Rating from '..'; + +export default { + title: 'WooCommerce Admin/components/Rating', + component: Rating, + args: { + rating: 4.5, + totalStars: 5, + size: 18, + }, +}; + +export const Default = ( args ) => ; diff --git a/packages/js/components/src/rating/test/__snapshots__/index.js.snap b/packages/js/components/src/rating/test/__snapshots__/index.tsx.snap similarity index 100% rename from packages/js/components/src/rating/test/__snapshots__/index.js.snap rename to packages/js/components/src/rating/test/__snapshots__/index.tsx.snap diff --git a/packages/js/components/src/rating/test/index.js b/packages/js/components/src/rating/test/index.js deleted file mode 100644 index 56d56918a5f..00000000000 --- a/packages/js/components/src/rating/test/index.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * External dependencies - */ -import { render } from '@testing-library/react'; -import StarIcon from 'gridicons/dist/star'; -import StarOutlineIcon from 'gridicons/dist/star-outline'; -import { createElement } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import Rating from '../'; -import ProductRating from '../product'; -import ReviewRating from '../review'; - -describe( 'Rating', () => { - test( 'should render the passed rating prop', () => { - const { container } = render( ); - expect( container ).toMatchSnapshot(); - } ); - - test( 'should render the correct amount of total stars', () => { - const { container } = render( - - ); - expect( container ).toMatchSnapshot(); - } ); - - test( 'should render stars at a different size', () => { - const { container } = render( ); - expect( container ).toMatchSnapshot(); - } ); - - test( 'should render different icons if specified', () => { - const { container } = render( - - ); - expect( container ).toMatchSnapshot(); - } ); -} ); - -describe( 'ReviewRating', () => { - test( 'should render rating based on review object', () => { - const review = { - review: 'Nice T-shirt!', - rating: 1.5, - }; - const { container } = render( ); - expect( container ).toMatchSnapshot(); - } ); -} ); - -describe( 'ProductRating', () => { - test( 'should render rating based on product object', () => { - const product = { - name: 'Test Product', - average_rating: 2.5, - }; - const { container } = render( ); - expect( container ).toMatchSnapshot(); - } ); -} ); diff --git a/packages/js/components/src/rating/test/index.tsx b/packages/js/components/src/rating/test/index.tsx new file mode 100644 index 00000000000..e86c9e0248e --- /dev/null +++ b/packages/js/components/src/rating/test/index.tsx @@ -0,0 +1,66 @@ +/** + * External dependencies + */ +import { render } from '@testing-library/react'; +import StarIcon from 'gridicons/dist/star'; +import StarOutlineIcon from 'gridicons/dist/star-outline'; +import { createElement } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import Rating from '..'; +import ProductRating from '../product'; +import ReviewRating from '../review'; + +describe( 'Rating', () => { + test( 'should render the passed rating prop', () => { + const { container } = render( ); + expect( container ).toMatchSnapshot(); + } ); + + test( 'should render the correct amount of total stars', () => { + const { container } = render( + + ); + expect( container ).toMatchSnapshot(); + } ); + + test( 'should render stars at a different size', () => { + const { container } = render( ); + expect( container ).toMatchSnapshot(); + } ); + + test( 'should render different icons if specified', () => { + const { container } = render( + + ); + expect( container ).toMatchSnapshot(); + } ); +} ); + +describe( 'ReviewRating', () => { + test( 'should render rating based on review object', () => { + const review = { + review: 'Nice T-shirt!', + rating: 1.5, + }; + const { container } = render( ); + expect( container ).toMatchSnapshot(); + } ); +} ); + +describe( 'ProductRating', () => { + test( 'should render rating based on product object', () => { + const product = { + name: 'Test Product', + average_rating: 2.5, + }; + const { container } = render( ); + expect( container ).toMatchSnapshot(); + } ); +} ); diff --git a/packages/js/components/src/rich-text-editor/editor-writing-flow.tsx b/packages/js/components/src/rich-text-editor/editor-writing-flow.tsx index b56a6ef08fd..5ee9e57dbdf 100644 --- a/packages/js/components/src/rich-text-editor/editor-writing-flow.tsx +++ b/packages/js/components/src/rich-text-editor/editor-writing-flow.tsx @@ -3,8 +3,8 @@ */ import { useSelect, useDispatch } from '@wordpress/data'; import { useInstanceId } from '@wordpress/compose'; +import { BlockInstance, createBlock } from '@wordpress/blocks'; import { createElement, useEffect } from '@wordpress/element'; -import { createBlock } from '@wordpress/blocks'; import { BlockList, ObserveTyping, @@ -15,37 +15,74 @@ import { WritingFlow, } from '@wordpress/block-editor'; -export const EditorWritingFlow: React.VFC = () => { +type EditorWritingFlowProps = { + blocks: BlockInstance[]; + onChange: ( changes: BlockInstance[] ) => void; + placeholder?: string; +}; + +export const EditorWritingFlow = ( { + blocks, + onChange, + placeholder = '', +}: EditorWritingFlowProps ) => { const instanceId = useInstanceId( EditorWritingFlow ); + const firstBlock = blocks[ 0 ]; + const isEmpty = ! blocks.length; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore This action is available in the block editor data store. - const { insertBlock } = useDispatch( blockEditorStore ); - - const { isEmpty } = useSelect( ( select ) => { - const blocks = select( 'core/block-editor' ).getBlocks(); + const { insertBlock, selectBlock, __unstableSetEditorMode } = + useDispatch( blockEditorStore ); + const { selectedBlockClientIds, editorMode } = useSelect( ( select ) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore This selector is available in the block editor data store. - const { getSelectedBlockClientIds } = select( blockEditorStore ); - + const { getSelectedBlockClientIds, __unstableGetEditorMode } = + select( blockEditorStore ); return { - isEmpty: blocks.length - ? blocks.length <= 1 && - blocks[ 0 ].attributes?.content?.trim() === '' - : true, - firstBlock: blocks[ 0 ], + editorMode: __unstableGetEditorMode(), selectedBlockClientIds: getSelectedBlockClientIds(), }; } ); + // This is a workaround to prevent focusing the block on intialization. + // Changing to a mode other than "edit" ensures that no initial position + // is found and no element gets subsequently focused. + // See https://github.com/WordPress/gutenberg/blob/411b6eee8376e31bf9db4c15c92a80524ae38e9b/packages/block-editor/src/components/block-list/use-block-props/use-focus-first-element.js#L42 + const setEditorIsInitializing = ( isInitializing: boolean ) => { + if ( typeof __unstableSetEditorMode !== 'function' ) { + return; + } + + __unstableSetEditorMode( isInitializing ? 'initialized' : 'edit' ); + }; + + useEffect( () => { + if ( selectedBlockClientIds?.length || ! firstBlock ) { + return; + } + + setEditorIsInitializing( true ); + selectBlock( firstBlock.clientId ); + }, [ firstBlock, selectedBlockClientIds ] ); + useEffect( () => { if ( isEmpty ) { const initialBlock = createBlock( 'core/paragraph', { content: '', + placeholder, } ); insertBlock( initialBlock ); + onChange( [ initialBlock ] ); } - }, [] ); + }, [ isEmpty ] ); + + const maybeSetEditMode = () => { + if ( editorMode === 'edit' ) { + return; + } + setEditorIsInitializing( false ); + }; return ( /* Gutenberg handles the keyboard events when focusing the content editable area. */ @@ -58,10 +95,13 @@ export const EditorWritingFlow: React.VFC = () => { } } > - + - { /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ } - { /* @ts-ignore This action is available in the block editor data store. */ } diff --git a/packages/js/components/src/rich-text-editor/rich-text-editor.tsx b/packages/js/components/src/rich-text-editor/rich-text-editor.tsx index d256503271c..3920a283970 100644 --- a/packages/js/components/src/rich-text-editor/rich-text-editor.tsx +++ b/packages/js/components/src/rich-text-editor/rich-text-editor.tsx @@ -1,18 +1,14 @@ /** * External dependencies */ -import { BaseControl, SlotFillProvider } from '@wordpress/components'; +import { BaseControl, Popover, SlotFillProvider } from '@wordpress/components'; import { BlockEditorProvider } from '@wordpress/block-editor'; import { BlockInstance } from '@wordpress/blocks'; +import { createElement, useEffect, useState, useRef } from '@wordpress/element'; import { debounce } from 'lodash'; -import { - createElement, - useCallback, - useEffect, - useState, - useRef, -} from '@wordpress/element'; import React from 'react'; +import { uploadMedia } from '@wordpress/media-utils'; +import { useUser } from '@woocommerce/data'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore No types for this exist yet. // eslint-disable-next-line @woocommerce/dependency-group @@ -31,15 +27,17 @@ type RichTextEditorProps = { label?: string; onChange: ( changes: BlockInstance[] ) => void; entryId?: string; + placeholder?: string; }; export const RichTextEditor: React.VFC< RichTextEditorProps > = ( { blocks, label, onChange, + placeholder = '', } ) => { const blocksRef = useRef( blocks ); - + const { currentUserCan } = useUser(); const [ , setRefresh ] = useState( 0 ); // If there is a props change we need to update the ref and force re-render. @@ -61,6 +59,24 @@ export const RichTextEditor: React.VFC< RichTextEditorProps > = ( { forceRerender(); }, 200 ); + const mediaUpload = currentUserCan( 'upload_files' ) + ? ( { + onError, + ...rest + }: { + onError: ( message: string ) => void; + } ) => { + uploadMedia( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore The upload function passes the remaining required props. + { + onError: ( { message } ) => onError( message ), + ...rest, + } + ); + } + : undefined; + return (
    { label && ( @@ -75,13 +91,19 @@ export const RichTextEditor: React.VFC< RichTextEditorProps > = ( { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore This property was recently added in the block editor data store. __experimentalClearBlockSelection: false, + mediaUpload, } } onInput={ debounceChange } onChange={ debounceChange } > - + +
    diff --git a/packages/js/components/src/rich-text-editor/style.scss b/packages/js/components/src/rich-text-editor/style.scss index 47b277c8075..722e98d31bf 100644 --- a/packages/js/components/src/rich-text-editor/style.scss +++ b/packages/js/components/src/rich-text-editor/style.scss @@ -1,3 +1,5 @@ +$toolbar-height: 40px; + .woocommerce-rich-text-editor { .woocommerce-rich-text-editor__writing-flow { border: 1px solid $gray-600; @@ -9,21 +11,12 @@ box-sizing: border-box; } - .block-editor-inserter { - display: none; - } - .block-editor-block-contextual-toolbar.is-fixed, .block-editor-block-contextual-toolbar.is-fixed .block-editor-block-toolbar .components-toolbar-group, .block-editor-block-contextual-toolbar.is-fixed .block-editor-block-toolbar .components-toolbar { border-color: $gray-600; } - /* Hide rich text placeholder text */ - .rich-text [data-rich-text-placeholder] { - display: none !important; - } - /* hide block boundary background styling */ .rich-text:focus *[data-rich-text-format-boundary] { background: none !important; @@ -39,11 +32,6 @@ outline: none; } - .block-editor-block-list__empty-block-inserter, - .block-editor-block-list__insertion-point { - display: none !important; - } - .block-editor-writing-flow { padding: $gap-small; } @@ -56,11 +44,25 @@ } .components-accessible-toolbar { + height: $toolbar-height; width: 100%; background-color: $white; border-color: $gray-700; border-bottom-left-radius: 0; border-bottom-right-radius: 0; + + .components-button { + height: $toolbar-height; + } + } + + .block-editor-block-mover:not(.is-horizontal) .block-editor-block-mover__move-button-container > * { + height: calc( $toolbar-height / 2 ); + } + + .block-editor-block-contextual-toolbar.is-fixed, + .components-toolbar-group { + min-height: $toolbar-height; } .wp-block-quote { diff --git a/packages/js/components/src/rich-text-editor/utils/register-blocks.ts b/packages/js/components/src/rich-text-editor/utils/register-blocks.ts index cdcf514835e..905a89e4bdb 100644 --- a/packages/js/components/src/rich-text-editor/utils/register-blocks.ts +++ b/packages/js/components/src/rich-text-editor/utils/register-blocks.ts @@ -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. @@ -14,6 +14,8 @@ export const HEADING_BLOCK_ID = 'core/heading'; export const LIST_BLOCK_ID = 'core/list'; export const LIST_ITEM_BLOCK_ID = 'core/list-item'; export const QUOTE_BLOCK_ID = 'core/quote'; +export const IMAGE_BLOCK_ID = 'core/image'; +export const VIDEO_BLOCK_ID = 'core/video'; const ALLOWED_CORE_BLOCKS = [ PARAGRAPH_BLOCK_ID, @@ -21,13 +23,16 @@ const ALLOWED_CORE_BLOCKS = [ LIST_BLOCK_ID, LIST_ITEM_BLOCK_ID, QUOTE_BLOCK_ID, + IMAGE_BLOCK_ID, + VIDEO_BLOCK_ID, ]; 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. diff --git a/packages/js/components/src/search-list-control/index.js b/packages/js/components/src/search-list-control/index.js index aa48b856867..f95f73032f7 100644 --- a/packages/js/components/src/search-list-control/index.js +++ b/packages/js/components/src/search-list-control/index.js @@ -82,20 +82,6 @@ export const SearchListControl = ( props ) => { }; }; - const onSelect = ( item ) => { - return () => { - if ( isSelected( item ) ) { - onRemove( item.id )(); - return; - } - if ( isSingle ) { - onChange( [ item ] ); - } else { - onChange( [ ...selected, item ] ); - } - }; - }; - const isSelected = ( item ) => findIndex( selected, { id: item.id } ) !== -1; @@ -114,6 +100,20 @@ export const SearchListControl = ( props ) => { : filteredList; }; + const onSelect = ( item ) => { + return () => { + if ( isSelected( item ) ) { + onRemove( item.id )(); + return; + } + if ( isSingle ) { + onChange( [ item ] ); + } else { + onChange( [ ...selected, item ] ); + } + }; + }; + const defaultRenderItem = ( args ) => { return ; }; diff --git a/packages/js/components/src/search-list-control/test/__snapshots__/index.js.snap b/packages/js/components/src/search-list-control/test/__snapshots__/index.js.snap index c404f59cb55..67b48f026a7 100644 --- a/packages/js/components/src/search-list-control/test/__snapshots__/index.js.snap +++ b/packages/js/components/src/search-list-control/test/__snapshots__/index.js.snap @@ -47,13 +47,13 @@ Object { class="woocommerce-search-list__search" >