Merge branch 'woocommerce:trunk' into add/36413-support-for-cart-checkout-in-declare-compatibility
This commit is contained in:
commit
2ed94d56c3
61
.codecov.yml
61
.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
|
||||
|
|
|
@ -14,15 +14,13 @@
|
|||
|
||||
Closes # .
|
||||
|
||||
<!-- The next section is mandatory. If your PR doesn't require testing, please indicate that you are purposefully omitting instructions. -->
|
||||
|
||||
- [ ] This PR is a very minor change/addition and does not require testing instructions (if checked you can ignore/remove the next section).
|
||||
|
||||
<!-- Begin testing instructions -->
|
||||
|
||||
### How to test the changes in this Pull Request:
|
||||
|
||||
<!-- Otherwise, please include detailed instructions on how these changes can be tested (including pre-conditions, configuration, steps to take and expected results). It may help to write your instructions using pseudocode -- as if you're telling a computer how to execute the test. -->
|
||||
<!-- Please include detailed instructions on how these changes can be tested, make sure to review and follow the guide for writing high-quality testing instructions below. -->
|
||||
|
||||
- [ ] Have you followed the [Writing high-quality testing instructions guide](https://github.com/woocommerce/woocommerce/wiki/Writing-high-quality-testing-instructions)?
|
||||
|
||||
1.
|
||||
2.
|
||||
|
@ -35,6 +33,7 @@ Closes # .
|
|||
- [ ] Have you added an explanation of what your changes do and why you'd like us to include them?
|
||||
- [ ] Have you written new tests for your changes, as applicable?
|
||||
- [ ] Have you created a changelog file for each project being changed, ie `pnpm --filter=<project> changelog add`?
|
||||
- [ ] Have you included testing instructions?
|
||||
|
||||
<!-- Mark completed items with an [x] -->
|
||||
|
||||
|
|
|
@ -27,26 +27,26 @@ runs:
|
|||
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
|
||||
uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd
|
||||
with:
|
||||
version: '^7.13.3'
|
||||
version: '^7.22.0'
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93
|
||||
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: pnpm
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@e04e1d97f0c0481c6e1ba40f8a538454fe5d7709
|
||||
uses: shivammathur/setup-php@8e2ac35f639d3e794c1da1f28999385ab6fdf0fc
|
||||
with:
|
||||
php-version: ${{ inputs.php-version }}
|
||||
coverage: none
|
||||
tools: phpcs, sirbrillig/phpcs-changed
|
||||
|
||||
- name: Cache Composer Dependencies
|
||||
uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77
|
||||
uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12
|
||||
with:
|
||||
path: ~/.cache/composer/files
|
||||
key: ${{ runner.os }}-php-${{ inputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
|
@ -59,7 +59,7 @@ runs:
|
|||
pnpm install ${{ steps.parse-input.outputs.INSTALL_FILTERS }}
|
||||
|
||||
- name: Cache Build Output
|
||||
uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77
|
||||
uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12
|
||||
with:
|
||||
path: node_modules/.cache/turbo
|
||||
key: ${{ runner.os }}-build-output-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}
|
||||
|
|
|
@ -17,14 +17,14 @@ jobs:
|
|||
name: Verify
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93
|
||||
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c
|
||||
|
||||
- name: Install Octokit
|
||||
run: npm --prefix .github/workflows/scripts install @octokit/action
|
||||
|
|
|
@ -118,102 +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
|
||||
# permissions:
|
||||
# contents: read
|
||||
# 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
|
||||
|
|
|
@ -23,7 +23,7 @@ jobs:
|
|||
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
|
||||
|
|
|
@ -13,63 +13,50 @@ concurrency:
|
|||
permissions: {}
|
||||
|
||||
jobs:
|
||||
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: '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.0'
|
||||
php: 7.4
|
||||
- wp: '5.9'
|
||||
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:
|
||||
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
|
||||
|
|
|
@ -14,8 +14,8 @@ jobs:
|
|||
name: Run prepare script
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
|
|
@ -1,43 +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
|
||||
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@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 }}
|
||||
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 }}
|
||||
|
|
|
@ -21,8 +21,8 @@ jobs:
|
|||
create-changelog-prs:
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
@ -46,6 +46,9 @@ jobs:
|
|||
- 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)
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ jobs:
|
|||
freeze: ${{ steps.check-freeze.outputs.freeze }}
|
||||
steps:
|
||||
- name: 'Install PHP'
|
||||
uses: shivammathur/setup-php@v2
|
||||
uses: shivammathur/setup-php@8e2ac35f639d3e794c1da1f28999385ab6fdf0fc
|
||||
with:
|
||||
php-version: '7.4'
|
||||
|
||||
|
@ -60,7 +60,8 @@ jobs:
|
|||
name: 'Maybe create next milestone and release branch'
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write
|
||||
issues: write
|
||||
needs: verify-code-freeze
|
||||
if: needs.verify-code-freeze.outputs.freeze == 0
|
||||
outputs:
|
||||
|
@ -89,8 +90,8 @@ jobs:
|
|||
name: Preps trunk for next development cycle
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
needs: maybe-create-next-milestone-and-release-branch
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
@ -159,7 +160,7 @@ jobs:
|
|||
name: 'Trigger changelog action'
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
actions: write
|
||||
actions: write
|
||||
needs: maybe-create-next-milestone-and-release-branch
|
||||
steps:
|
||||
- name: 'Trigger changelog action'
|
||||
|
|
|
@ -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
|
|
@ -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';
|
||||
|
|
|
@ -17,11 +17,56 @@ concurrency:
|
|||
permissions: {}
|
||||
|
||||
jobs:
|
||||
api-tests:
|
||||
name: API tests on nightly build
|
||||
runs-on: ubuntu-20.04
|
||||
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
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ env.BRANCH_NAME }}
|
||||
|
||||
- name: Setup WooCommerce Monorepo
|
||||
uses: ./.github/actions/setup-woocommerce-monorepo
|
||||
with:
|
||||
install-filters: woocommerce
|
||||
build: false
|
||||
|
||||
- name: Run API tests.
|
||||
working-directory: plugins/woocommerce
|
||||
env:
|
||||
BASE_URL: ${{ secrets.SMOKE_TEST_URL }}
|
||||
USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
|
||||
USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
|
||||
DEFAULT_TIMEOUT_OVERRIDE: 120000
|
||||
run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello.test.js
|
||||
|
||||
- name: Generate API Test report.
|
||||
if: success() || failure()
|
||||
working-directory: plugins/woocommerce
|
||||
run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }}
|
||||
|
||||
- name: Archive API test report
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.API_ARTIFACT }}
|
||||
path: |
|
||||
${{ env.ALLURE_RESULTS_DIR }}
|
||||
${{ env.ALLURE_REPORT_DIR }}
|
||||
if-no-files-found: ignore
|
||||
retention-days: 5
|
||||
|
||||
e2e-tests:
|
||||
name: E2E tests on nightly build
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: read
|
||||
needs: [api-tests]
|
||||
env:
|
||||
ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
|
||||
ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
|
||||
|
@ -78,52 +123,6 @@ jobs:
|
|||
if-no-files-found: ignore
|
||||
retention-days: 5
|
||||
|
||||
api-tests:
|
||||
name: API tests on nightly build
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: read
|
||||
needs: [e2e-tests]
|
||||
if: success() || failure()
|
||||
env:
|
||||
ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-results
|
||||
ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/test-results/allure-report
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ env.BRANCH_NAME }}
|
||||
|
||||
- name: Setup WooCommerce Monorepo
|
||||
uses: ./.github/actions/setup-woocommerce-monorepo
|
||||
with:
|
||||
install-filters: woocommerce
|
||||
build: false
|
||||
|
||||
- name: Run API tests.
|
||||
working-directory: plugins/woocommerce
|
||||
env:
|
||||
BASE_URL: ${{ secrets.SMOKE_TEST_URL }}
|
||||
USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
|
||||
USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
|
||||
DEFAULT_TIMEOUT_OVERRIDE: 120000
|
||||
run: pnpm exec playwright test --config=tests/api-core-tests/playwright.config.js hello.test.js
|
||||
|
||||
- name: Generate API Test report.
|
||||
if: success() || failure()
|
||||
working-directory: plugins/woocommerce
|
||||
run: pnpm exec allure generate --clean ${{ env.ALLURE_RESULTS_DIR }} --output ${{ env.ALLURE_REPORT_DIR }}
|
||||
|
||||
- name: Archive API test report
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.API_ARTIFACT }}
|
||||
path: |
|
||||
${{ env.ALLURE_RESULTS_DIR }}
|
||||
${{ env.ALLURE_REPORT_DIR }}
|
||||
if-no-files-found: ignore
|
||||
retention-days: 5
|
||||
|
||||
k6-tests:
|
||||
name: k6 tests on nightly build
|
||||
runs-on: ubuntu-20.04
|
||||
|
@ -181,6 +180,7 @@ jobs:
|
|||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: read
|
||||
needs: [api-tests]
|
||||
env:
|
||||
USE_WP_ENV: 1
|
||||
ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/e2e-pw/allure-results
|
||||
|
@ -256,7 +256,7 @@ jobs:
|
|||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: read
|
||||
needs: [test-plugins, k6-tests]
|
||||
needs: [e2e-tests, test-plugins, k6-tests]
|
||||
steps:
|
||||
- name: Create dirs
|
||||
run: |
|
||||
|
@ -312,7 +312,7 @@ jobs:
|
|||
( success() || failure() ) &&
|
||||
! github.event.pull_request.head.repo.fork
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [test-plugins, k6-tests]
|
||||
needs: [e2e-tests, test-plugins, k6-tests]
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.REPORTS_TOKEN }}
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
|
|
|
@ -1,37 +1,37 @@
|
|||
name: Synchronize Dependencies with syncpack
|
||||
on:
|
||||
# Run whenever a pull request is updated
|
||||
pull_request:
|
||||
branches:
|
||||
- trunk
|
||||
paths:
|
||||
- '**/package.json'
|
||||
# Run whenever a pull request is updated
|
||||
pull_request:
|
||||
branches:
|
||||
- trunk
|
||||
paths:
|
||||
- '**/package.json'
|
||||
|
||||
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@v3
|
||||
with:
|
||||
node-version: 16
|
||||
syncpack:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
name: syncpack
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- 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
|
||||
- name: 'Setup node'
|
||||
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- 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
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
pnpm install
|
23
.syncpackrc
23
.syncpackrc
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"dev": true,
|
||||
"filter": "^(?:react|react-dom|typescript|@typescript-eslint|@types/react).*$",
|
||||
"filter": "^(?:config|react|react-dom|eslint|typescript|@typescript-eslint|@types/react).*$",
|
||||
"indent": "\t",
|
||||
"overrides": true,
|
||||
"peer": true,
|
||||
|
@ -33,6 +33,15 @@
|
|||
"**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dependencies": [
|
||||
"config"
|
||||
],
|
||||
"packages": [
|
||||
"**"
|
||||
],
|
||||
"pinVersion": "3.3.7"
|
||||
},
|
||||
{
|
||||
"dependencies": [
|
||||
"react",
|
||||
|
@ -51,6 +60,18 @@
|
|||
"**"
|
||||
],
|
||||
"pinVersion": "^4.8.3"
|
||||
},
|
||||
{
|
||||
"dependencies": [
|
||||
"eslint"
|
||||
],
|
||||
"dependencyTypes": [
|
||||
"devDependencies"
|
||||
],
|
||||
"packages": [
|
||||
"**"
|
||||
],
|
||||
"pinVersion": "^8.32.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -31,8 +31,9 @@ if [ $? -ne 0 ]; then
|
|||
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
|
||||
|
|
230
changelog.txt
230
changelog.txt
|
@ -1,5 +1,235 @@
|
|||
== Changelog ==
|
||||
|
||||
= 7.4.0 2023-02-14 =
|
||||
|
||||
**WooCommerce**
|
||||
|
||||
* Fix - Add support for sorting by includes param. [#36215](https://github.com/woocommerce/woocommerce/pull/36215)
|
||||
* Fix - Allow product tab navigation without prompting for unsaved changes [#36235](https://github.com/woocommerce/woocommerce/pull/36235)
|
||||
* Fix - Convert HTML to blocks in product variation description [#36241](https://github.com/woocommerce/woocommerce/pull/36241)
|
||||
* Fix - Decode HTML entities in CategoryBreadcrumbs. [#36321](https://github.com/woocommerce/woocommerce/pull/36321)
|
||||
* Fix - Decode HTML entities in CategoryFieldItem. [#36367](https://github.com/woocommerce/woocommerce/pull/36367)
|
||||
* Fix - Ensure order emails are responsive in most email clients, including when the current language is RTL. [#36310](https://github.com/woocommerce/woocommerce/pull/36310)
|
||||
* Fix - Ensures product variation sort order is correctly persisted. [#36343](https://github.com/woocommerce/woocommerce/pull/36343)
|
||||
* Fix - Ensure wc_get_order() works without arguments when HPOS is enabled. [#36496](https://github.com/woocommerce/woocommerce/pull/36496)
|
||||
* Fix - Fix "Save changes?" modal saves the options after selecting the 'Discard' option [#36160](https://github.com/woocommerce/woocommerce/pull/36160)
|
||||
* Fix - Fix attributes/options lists corrupt render #36236 [#36236](https://github.com/woocommerce/woocommerce/pull/36236)
|
||||
* Fix - Fix bug when filtering for customer_id=0. [#36216](https://github.com/woocommerce/woocommerce/pull/36216)
|
||||
* Fix - Fix deprecated usage of ${var} in strings [#36439](https://github.com/woocommerce/woocommerce/pull/36439)
|
||||
* Fix - Fix edit attribute modal terms list [#36186](https://github.com/woocommerce/woocommerce/pull/36186)
|
||||
* Fix - Fixes editing of child product reviews. [#35888](https://github.com/woocommerce/woocommerce/pull/35888)
|
||||
* Fix - Fix for product filters when 'shop' page is the front page. [#36224](https://github.com/woocommerce/woocommerce/pull/36224)
|
||||
* Fix - Fix issue where attribute term dropdown was not adhering to sort order setting. [#36047](https://github.com/woocommerce/woocommerce/pull/36047)
|
||||
* Fix - Fix navigation between variations and tab selection [#36239](https://github.com/woocommerce/woocommerce/pull/36239)
|
||||
* Fix - Fix notices styling in Twenty Twenty-Three [#36475](https://github.com/woocommerce/woocommerce/pull/36475)
|
||||
* Fix - Fix overlapping header elements on product page [#36495](https://github.com/woocommerce/woocommerce/pull/36495)
|
||||
* Fix - Fix product table dropdown issue on mobile. [#36046](https://github.com/woocommerce/woocommerce/pull/36046)
|
||||
* Fix - Fix reordering list items error [#36296](https://github.com/woocommerce/woocommerce/pull/36296)
|
||||
* Fix - Fix REST API order refunds enpoint when HPOS is active, and make v2 orders endpoint compatible with HPOS [#36308](https://github.com/woocommerce/woocommerce/pull/36308)
|
||||
* Fix - Fix settings tables styles [#36531](https://github.com/woocommerce/woocommerce/pull/36531)
|
||||
* Fix - Fix tax task showing as not completed after setting up tax [#36468](https://github.com/woocommerce/woocommerce/pull/36468)
|
||||
* Fix - Fix the signature mismatch affecting wc cli commands ability to fetch user subscription data. [#36240](https://github.com/woocommerce/woocommerce/pull/36240)
|
||||
* Fix - Fix total count query of orders within Analytics reports data store. [#35971](https://github.com/woocommerce/woocommerce/pull/35971)
|
||||
* Fix - Hide Variations section when it is empty [#36202](https://github.com/woocommerce/woocommerce/pull/36202)
|
||||
* Fix - Improve accessibility of the coupon code label, in the context of the cart page. [#36247](https://github.com/woocommerce/woocommerce/pull/36247)
|
||||
* Fix - Improve the way we retrieve the alt text property for product attachments. [#35009](https://github.com/woocommerce/woocommerce/pull/35009)
|
||||
* Fix - Load wc_empty_cart function for REST API calls. [#36182](https://github.com/woocommerce/woocommerce/pull/36182)
|
||||
* Fix - Make HPOS UX more consistent with posts UI (so that same e2e tests passes for both). [#36282](https://github.com/woocommerce/woocommerce/pull/36282)
|
||||
* Fix - Make order edit messages compatible with both posts and theorder object. [#36485](https://github.com/woocommerce/woocommerce/pull/36485)
|
||||
* Fix - Make sure the tracking shortcode only operates in orders with billing information. [#33735](https://github.com/woocommerce/woocommerce/pull/33735)
|
||||
* Fix - Remove persisted query on return to parent product from variation [#36365](https://github.com/woocommerce/woocommerce/pull/36365)
|
||||
* Fix - Reset variation form if a new variation is given [#36078](https://github.com/woocommerce/woocommerce/pull/36078)
|
||||
* Fix - Restore the pre-7.2.0 behavior for single product quantity inputs. [#36460](https://github.com/woocommerce/woocommerce/pull/36460)
|
||||
* Fix - Set child orders to be children of current order parent before deleting for consistency. [#36218](https://github.com/woocommerce/woocommerce/pull/36218)
|
||||
* Fix - Skip custom search for HPOS API queries as it's handled already. [#36213](https://github.com/woocommerce/woocommerce/pull/36213)
|
||||
* Fix - Use Imagick functions to set parallel thread count instead of direct putenv call as suggested in https://core.trac.wordpress.org/ticket/36534#comment:129. [#35339](https://github.com/woocommerce/woocommerce/pull/35339)
|
||||
* Fix - When adjusting download permissions, confirm the child products have not been removed. [#36431](https://github.com/woocommerce/woocommerce/pull/36431)
|
||||
* Add - Add ability to filter variations by local attributes in REST API [#36201](https://github.com/woocommerce/woocommerce/pull/36201)
|
||||
* Add - Add an admin notice about the upcoming PHP version requirement change for PHP 7.2 users [#36444](https://github.com/woocommerce/woocommerce/pull/36444)
|
||||
* Add - Added a slot for extending the app with a homescreen header banner [#36467](https://github.com/woocommerce/woocommerce/pull/36467)
|
||||
* Add - Added a slot for ProgressHeader and ProgressTitle component [#36482](https://github.com/woocommerce/woocommerce/pull/36482)
|
||||
* Add - Add edit button to variations list items [#36079](https://github.com/woocommerce/woocommerce/pull/36079)
|
||||
* Add - Added slot for tasklist completion slotfill [#36487](https://github.com/woocommerce/woocommerce/pull/36487)
|
||||
* Add - Add endpoint to create all product variations [#35980](https://github.com/woocommerce/woocommerce/pull/35980)
|
||||
* Add - Add exit prompt CES for users editing orders when tracking is enabled. [#35762](https://github.com/woocommerce/woocommerce/pull/35762)
|
||||
* Add - Adding delayed spotlight to feedback button on current product page. [#35865](https://github.com/woocommerce/woocommerce/pull/35865)
|
||||
* Add - Adding feedback button to activity bar on classic product page. [#35810](https://github.com/woocommerce/woocommerce/pull/35810)
|
||||
* Add - Adding JS data store for ProductForm. [#36430](https://github.com/woocommerce/woocommerce/pull/36430)
|
||||
* Add - Adding the WooProductSectionItem slot within the product editor general tab. [#36331](https://github.com/woocommerce/woocommerce/pull/36331)
|
||||
* Add - Add initial product form PHP helper class to add new fields. [#36093](https://github.com/woocommerce/woocommerce/pull/36093)
|
||||
* Add - Additional error logging within the CSV Exporter framework. [#34802](https://github.com/woocommerce/woocommerce/pull/34802)
|
||||
* Add - Add multichannel marketing API [#36453](https://github.com/woocommerce/woocommerce/pull/36453)
|
||||
* Add - Add new filter to add additional clauses for SQL statement in Variations report [#36378](https://github.com/woocommerce/woocommerce/pull/36378)
|
||||
* Add - Add new product form API for extending the new Product Form MVP. [#36165](https://github.com/woocommerce/woocommerce/pull/36165)
|
||||
* Add - Add Options section to new product experience form. [#35910](https://github.com/woocommerce/woocommerce/pull/35910)
|
||||
* Add - Add product tour to new product management experience [#36428](https://github.com/woocommerce/woocommerce/pull/36428)
|
||||
* Add - Add product variation form [#36033](https://github.com/woocommerce/woocommerce/pull/36033)
|
||||
* Add - Add product variation General section [#36081](https://github.com/woocommerce/woocommerce/pull/36081)
|
||||
* Add - Add product variation header actions and persistence [#36155](https://github.com/woocommerce/woocommerce/pull/36155)
|
||||
* Add - Add product variation image [#36133](https://github.com/woocommerce/woocommerce/pull/36133)
|
||||
* Add - Add product variation navigation component [#36076](https://github.com/woocommerce/woocommerce/pull/36076)
|
||||
* Add - Add product variations flag to only show work in development [#36311](https://github.com/woocommerce/woocommerce/pull/36311)
|
||||
* Add - Add product variation title to page header [#36085](https://github.com/woocommerce/woocommerce/pull/36085)
|
||||
* Add - Add Product variation visibility toggle [#36020](https://github.com/woocommerce/woocommerce/pull/36020)
|
||||
* Add - Add single product variation sections [#36051](https://github.com/woocommerce/woocommerce/pull/36051)
|
||||
* Add - Adds support for a 'required' argument when invoking `wc_dropdown_variation_attribute_options()`. [#34579](https://github.com/woocommerce/woocommerce/pull/34579)
|
||||
* Add - Add support for sorting by order metadata in HPOS queries. [#36403](https://github.com/woocommerce/woocommerce/pull/36403)
|
||||
* Add - Add WooOnboardingTaskListHeader, woocommerce_admin_experimental_onboarding_tasklists filter, and woocommerce_onboarding_task_list_header Slot to task list [#36519](https://github.com/woocommerce/woocommerce/pull/36519)
|
||||
* Add - Include tax options in pricing section [#36299](https://github.com/woocommerce/woocommerce/pull/36299)
|
||||
* Add - Persist active tab on refresh [#36112](https://github.com/woocommerce/woocommerce/pull/36112)
|
||||
* Add - Persist variations order on product save [#36109](https://github.com/woocommerce/woocommerce/pull/36109)
|
||||
* Add - Product variation quantity status indicator [#35982](https://github.com/woocommerce/woocommerce/pull/35982)
|
||||
* Add - Product variations card should have a fixed height. [#36053](https://github.com/woocommerce/woocommerce/pull/36053)
|
||||
* Add - Remove manage_stock 'parent' value before saving the variation [#36234](https://github.com/woocommerce/woocommerce/pull/36234)
|
||||
* Add - Run ces exit prompt when product import abandoned. [#35996](https://github.com/woocommerce/woocommerce/pull/35996)
|
||||
* Add - Scroll newly added product attribute into view in new product management experience [#36447](https://github.com/woocommerce/woocommerce/pull/36447)
|
||||
* Add - Show product CES footer on product tour close [#36516](https://github.com/woocommerce/woocommerce/pull/36516)
|
||||
* Add - Truncate attribute option name to a max of 32 chars in variations list [#36134](https://github.com/woocommerce/woocommerce/pull/36134)
|
||||
* Add - Trying experimental slot context with product editor fills. [#36333](https://github.com/woocommerce/woocommerce/pull/36333)
|
||||
* Add - Using slotfill to insert attributes section in the product editor. [#36483](https://github.com/woocommerce/woocommerce/pull/36483)
|
||||
* Add - Using slotfill to insert images section in product editor. [#36461](https://github.com/woocommerce/woocommerce/pull/36461)
|
||||
* Update - Update woocommerce-blocks to 9.4.3. [#36736](https://github.com/woocommerce/woocommerce/pull/36736)
|
||||
* Update - Adding WooProductFieldItem slot to product details section. [#36315](https://github.com/woocommerce/woocommerce/pull/36315)
|
||||
* Update - Add permalink_template and generated_slug to products REST API response. [#36497](https://github.com/woocommerce/woocommerce/pull/36497)
|
||||
* Update - Auto generate variations on option changes [#36188](https://github.com/woocommerce/woocommerce/pull/36188)
|
||||
* Update - Bundled version of Action Scheduler updated to 3.5.4. [#36433](https://github.com/woocommerce/woocommerce/pull/36433)
|
||||
* Update - Customers REST API endpoint will now return user metadata only when requester has an administrator role [#36408](https://github.com/woocommerce/woocommerce/pull/36408)
|
||||
* Update - Disable irrelevant product tabs when variations exist [#35939](https://github.com/woocommerce/woocommerce/pull/35939)
|
||||
* Update - Migrate shipping section in product editor to slot fill. [#36534](https://github.com/woocommerce/woocommerce/pull/36534)
|
||||
* Update - Move product management feature flag down to experimental. [#36552](https://github.com/woocommerce/woocommerce/pull/36552)
|
||||
* Update - Reimplementing product details fields in product editor as slot fills. [#36368](https://github.com/woocommerce/woocommerce/pull/36368)
|
||||
* Update - Update api-core-tests readme to include a guide for writing tests [#35978](https://github.com/woocommerce/woocommerce/pull/35978)
|
||||
* Update - Update store-details test snapshot to reflect updated select-control [#35808](https://github.com/woocommerce/woocommerce/pull/35808)
|
||||
* Update - Update WooCommerce Blocks to 9.4.0 [#36524](https://github.com/woocommerce/woocommerce/pull/36524)
|
||||
* Update - Update WooCommerce Blocks to 9.4.1 [#36553](https://github.com/woocommerce/woocommerce/pull/36553)
|
||||
* Update - Update WooCommerce Blocks to 9.4.2 [#36624](https://github.com/woocommerce/woocommerce/pull/36624)
|
||||
* Dev - Add advanced setting option [#36380](https://github.com/woocommerce/woocommerce/pull/36380)
|
||||
* Dev - Add experimental SlotFill for task list footer [#36527](https://github.com/woocommerce/woocommerce/pull/36527)
|
||||
* Dev - Cleanup product task experiment [#35950](https://github.com/woocommerce/woocommerce/pull/35950)
|
||||
* Dev - Consistent folder structure for E2E and API test results [#35907](https://github.com/woocommerce/woocommerce/pull/35907)
|
||||
* Dev - Fix docblock type annotations for $meta_value. [#33853](https://github.com/woocommerce/woocommerce/pull/33853)
|
||||
* Dev - Fix flakiness of the `can save industry changes when navigating back to "Store Details"` E2E test. [#36260](https://github.com/woocommerce/woocommerce/pull/36260)
|
||||
* Dev - Make shopper tests passable on daily smoke test site. [#35873](https://github.com/woocommerce/woocommerce/pull/35873)
|
||||
* Dev - Move product attribute fetching logic into a separate hook [#36354](https://github.com/woocommerce/woocommerce/pull/36354)
|
||||
* Dev - Update TaskLists::add_task() to reflect changes in TaskList::add_task() [#36104](https://github.com/woocommerce/woocommerce/pull/36104)
|
||||
* Dev - Update the browserslist config for legacy client JS to match Wordpress. [#36264](https://github.com/woocommerce/woocommerce/pull/36264)
|
||||
* Dev - Upgrade PHPUnit to v8 [#36273](https://github.com/woocommerce/woocommerce/pull/36273)
|
||||
* Tweak - Corrects a typo in the i18n/states.php file, relating to our list of Iranian states. [#36457](https://github.com/woocommerce/woocommerce/pull/36457)
|
||||
* Tweak - Derive product type from product attributes [#36243](https://github.com/woocommerce/woocommerce/pull/36243)
|
||||
* Tweak - Fix typo in a function comment. [#36122](https://github.com/woocommerce/woocommerce/pull/36122)
|
||||
* Tweak - Fix units in function doc comment [#36353](https://github.com/woocommerce/woocommerce/pull/36353)
|
||||
* Tweak - Make related products check more robust against wrong transients. [#34742](https://github.com/woocommerce/woocommerce/pull/34742)
|
||||
* Tweak - Makes it possible to use an `add_meta_boxes_<SCREEN_ID>` style hook in the HPOS editor, for parity with the traditional post editor. [#35999](https://github.com/woocommerce/woocommerce/pull/35999)
|
||||
* Tweak - Minor adjustments to the ProductForm API [#36414](https://github.com/woocommerce/woocommerce/pull/36414)
|
||||
* Tweak - Redirect to new product experience when in experiment group [#36381](https://github.com/woocommerce/woocommerce/pull/36381)
|
||||
* Tweak - Refactor AttributeField into sub-components. [#35997](https://github.com/woocommerce/woocommerce/pull/35997)
|
||||
* Tweak - Update product links when new product management experience is enabled [#36382](https://github.com/woocommerce/woocommerce/pull/36382)
|
||||
* Tweak - Updates and improves the docblocks for methods WC_Order::get_total() and WC_Order::get_subtotal(). [#34385](https://github.com/woocommerce/woocommerce/pull/34385)
|
||||
* Tweak - Validation of Norweigan postcodes has been added. [#36277](https://github.com/woocommerce/woocommerce/pull/36277)
|
||||
* Performance - Speed up HPOS search query by using group by instead of distinct. [#35897](https://github.com/woocommerce/woocommerce/pull/35897)
|
||||
* Enhancement - Add context to countries shipping to prefix [#36254](https://github.com/woocommerce/woocommerce/pull/36254)
|
||||
* Enhancement - Adds new order status filters for bacs and cheque email instructions. [#35849](https://github.com/woocommerce/woocommerce/pull/35849)
|
||||
* Enhancement - Improves handling of the single product page quantity selector, in relation to variable products. [#36087](https://github.com/woocommerce/woocommerce/pull/36087)
|
||||
* Enhancement - Remove default WooCommerce button styles if using a block theme which adds button styles in theme.json [#36225](https://github.com/woocommerce/woocommerce/pull/36225)
|
||||
|
||||
|
||||
= 7.3.0 2023-01-10 =
|
||||
|
||||
**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**
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
"sass": "^1.49.9",
|
||||
"sass-loader": "^10.2.1",
|
||||
"syncpack": "^8.3.9",
|
||||
"turbo": "^1.4.5",
|
||||
"turbo": "^1.7.0",
|
||||
"typescript": "^4.8.3",
|
||||
"url-loader": "^1.1.2",
|
||||
"webpack": "^5.70.0"
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: dev
|
||||
|
||||
Update eslint to 8.32.0 across the monorepo.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: fix
|
||||
|
||||
Unify semver range for `config@3.3.7` (from `^3.3.7`). Fix `node_env_var_name is not defined` error.
|
|
@ -29,7 +29,7 @@
|
|||
"@jest/globals": "^27.5.1",
|
||||
"@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",
|
||||
|
@ -44,7 +44,7 @@
|
|||
"@typescript-eslint/eslint-plugin": "^5.43.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",
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@woocommerce/eslint-plugin": "workspace:*",
|
||||
"eslint": "^8.12.0"
|
||||
"eslint": "^8.32.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: dev
|
||||
|
||||
Update eslint to 8.32.0 across the monorepo.
|
|
@ -55,7 +55,7 @@
|
|||
"@typescript-eslint/parser": "^5.43.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"
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: dev
|
||||
|
||||
Create tree-control component
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: dev
|
||||
|
||||
Add TreeControl expand/collapse functionality.
|
|
@ -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.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Added LearnMore option as well as made it possible to use this button multiple instances on the page
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Move experimental product section components to @woocommerce/product-editor package.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: fix
|
||||
|
||||
Altering styles to correctly target fields within slot fills on product editor.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: add
|
||||
|
||||
Add an optional "InputProps" to experimental SelectControl component
|
|
@ -1,4 +0,0 @@
|
|||
Significance: minor
|
||||
Type: tweak
|
||||
|
||||
Update spelling of Cancelled to Canceled for US English.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: dev
|
||||
|
||||
Update eslint to 8.32.0 across the monorepo.
|
|
@ -1,4 +1,4 @@
|
|||
Significance: patch
|
||||
Type: dev
|
||||
|
||||
Update readme
|
||||
Migrate Table component to TS
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: fix
|
||||
|
||||
Refactor createOrderedChildren
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: fix
|
||||
|
||||
Fix issue were Options tab was not showing up anymore in new product management screen.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: fix
|
||||
|
||||
Include CSS for experimental tree control so it renders properly in Storybook.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: fix
|
||||
|
||||
Fix SelectControl and TreeControl styles.
|
|
@ -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
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add new WooProductTabItem component for slot filling tab items.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Adding experimental component SlotContext
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Updating the product editor fill components to support multiple targets.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Adding ProductSectionLayout component and changing default order for WooProductSectionItem component.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Updating WooProductFieldItem to uniquely generate IDs with different sections.
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Add deprecated message to packages moved to product-editor package.
|
|
@ -44,6 +44,7 @@
|
|||
"@woocommerce/navigation": "workspace:*",
|
||||
"@wordpress/a11y": "3.5.0",
|
||||
"@wordpress/api-fetch": "^6.0.1",
|
||||
"@wordpress/base-styles": "^4.3.0",
|
||||
"@wordpress/block-editor": "^9.8.0",
|
||||
"@wordpress/block-library": "^7.16.0",
|
||||
"@wordpress/blocks": "^11.18.0",
|
||||
|
@ -129,7 +130,7 @@
|
|||
"@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",
|
||||
|
@ -164,5 +165,11 @@
|
|||
"pnpm lint:fix",
|
||||
"pnpm test-staged"
|
||||
]
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
UseComboboxState,
|
||||
UseComboboxStateChangeOptions,
|
||||
useMultipleSelection,
|
||||
GetInputPropsOptions,
|
||||
} from 'downshift';
|
||||
import {
|
||||
useState,
|
||||
|
@ -65,6 +66,7 @@ export type SelectControlProps< ItemType > = {
|
|||
selected: ItemType | ItemType[] | null;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
inputProps?: GetInputPropsOptions;
|
||||
suffix?: JSX.Element | null;
|
||||
/**
|
||||
* This is a feature already implemented in downshift@7.0.0 through the
|
||||
|
@ -119,6 +121,7 @@ function SelectControl< ItemType = DefaultItemType >( {
|
|||
selected,
|
||||
className,
|
||||
disabled,
|
||||
inputProps = {},
|
||||
suffix = <SuffixIcon icon={ search } />,
|
||||
__experimentalOpenMenuOnFocus = false,
|
||||
}: SelectControlProps< ItemType > ) {
|
||||
|
@ -268,6 +271,7 @@ function SelectControl< ItemType = DefaultItemType >( {
|
|||
onBlur: () => setIsFocused( false ),
|
||||
placeholder,
|
||||
disabled,
|
||||
...inputProps,
|
||||
} ) }
|
||||
suffix={ suffix }
|
||||
>
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { TreeItemProps } from '../types';
|
||||
|
||||
export function useExpander( {
|
||||
shouldItemBeExpanded,
|
||||
item,
|
||||
}: Pick< TreeItemProps, 'shouldItemBeExpanded' | 'item' > ) {
|
||||
const [ isExpanded, setExpanded ] = useState( false );
|
||||
|
||||
useEffect( () => {
|
||||
if (
|
||||
item.children?.length &&
|
||||
typeof shouldItemBeExpanded === 'function'
|
||||
) {
|
||||
setExpanded( shouldItemBeExpanded( item ) );
|
||||
}
|
||||
}, [ item, shouldItemBeExpanded ] );
|
||||
|
||||
function onToggleExpand() {
|
||||
setExpanded( ( prev ) => ! prev );
|
||||
}
|
||||
|
||||
return { isExpanded, onToggleExpand };
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { TreeItemProps } from '../types';
|
||||
import { useExpander } from './use-expander';
|
||||
|
||||
export function useTreeItem( {
|
||||
item,
|
||||
level,
|
||||
shouldItemBeExpanded,
|
||||
...props
|
||||
}: TreeItemProps ) {
|
||||
const nextLevel = level + 1;
|
||||
|
||||
const expander = useExpander( {
|
||||
item,
|
||||
shouldItemBeExpanded,
|
||||
} );
|
||||
|
||||
return {
|
||||
item,
|
||||
level: nextLevel,
|
||||
expander,
|
||||
treeItemProps: {
|
||||
...props,
|
||||
},
|
||||
headingProps: {
|
||||
style: {
|
||||
'--level': level,
|
||||
} as React.CSSProperties,
|
||||
},
|
||||
treeProps: {
|
||||
items: item.children,
|
||||
level: nextLevel,
|
||||
shouldItemBeExpanded,
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { TreeProps } from '../types';
|
||||
|
||||
export function useTree( {
|
||||
ref,
|
||||
items,
|
||||
level = 1,
|
||||
shouldItemBeExpanded,
|
||||
...props
|
||||
}: TreeProps ) {
|
||||
return {
|
||||
level,
|
||||
items,
|
||||
treeProps: {
|
||||
...props,
|
||||
},
|
||||
treeItemProps: {
|
||||
level,
|
||||
shouldItemBeExpanded,
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export * from './tree';
|
||||
export * from './tree-control';
|
||||
export * from './tree-item';
|
||||
export * from './types';
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { BaseControl, TextControl } from '@wordpress/components';
|
||||
import React, { createElement, useCallback, useState } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { TreeControl } from '../tree-control';
|
||||
import { Item, LinkedTree } from '../types';
|
||||
|
||||
const listItems: Item[] = [
|
||||
{ value: '1', label: 'Technology' },
|
||||
{ value: '1.1', label: 'Notebooks', parent: '1' },
|
||||
{ value: '1.2', label: 'Phones', parent: '1' },
|
||||
{ value: '1.2.1', label: 'iPhone', parent: '1.2' },
|
||||
{ value: '1.2.1.1', label: 'iPhone 14 Pro', parent: '1.2.1' },
|
||||
{ value: '1.2.1.2', label: 'iPhone 14 Pro Max', parent: '1.2.1' },
|
||||
{ value: '1.2.2', label: 'Samsung', parent: '1.2' },
|
||||
{ value: '1.2.2.1', label: 'Samsung Galaxy 22 Plus', parent: '1.2.2' },
|
||||
{ value: '1.2.2.2', label: 'Samsung Galaxy 22 Ultra', parent: '1.2.2' },
|
||||
{ value: '1.3', label: 'Wearables', parent: '1' },
|
||||
{ value: '2', label: 'Hardware' },
|
||||
{ value: '2.1', label: 'CPU', parent: '2' },
|
||||
{ value: '2.2', label: 'GPU', parent: '2' },
|
||||
{ value: '2.3', label: 'Memory RAM', parent: '2' },
|
||||
{ value: '3', label: 'Other' },
|
||||
];
|
||||
|
||||
export const SimpleTree: React.FC = () => {
|
||||
return (
|
||||
<BaseControl label="Simple tree" id="simple-tree">
|
||||
<TreeControl id="simple-tree" items={ listItems } />
|
||||
</BaseControl>
|
||||
);
|
||||
};
|
||||
|
||||
function shouldItemBeExpanded( item: LinkedTree, filter: string ) {
|
||||
if ( ! filter || ! item.children?.length ) return false;
|
||||
return item.children.some( ( child ) => {
|
||||
if ( new RegExp( filter, 'ig' ).test( child.data.label ) ) {
|
||||
return true;
|
||||
}
|
||||
return shouldItemBeExpanded( child, filter );
|
||||
} );
|
||||
}
|
||||
|
||||
export const ExpandOnFilter: React.FC = () => {
|
||||
const [ filter, setFilter ] = useState( '' );
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextControl value={ filter } onChange={ setFilter } />
|
||||
<BaseControl label="Expand on filter" id="expand-on-filter">
|
||||
<TreeControl
|
||||
id="expand-on-filter"
|
||||
items={ listItems }
|
||||
shouldItemBeExpanded={ ( item ) =>
|
||||
shouldItemBeExpanded( item, filter )
|
||||
}
|
||||
/>
|
||||
</BaseControl>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Admin/experimental/TreeControl',
|
||||
component: TreeControl,
|
||||
};
|
|
@ -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 <Tree { ...props } ref={ ref } items={ linkedTree } />;
|
||||
} );
|
|
@ -0,0 +1,42 @@
|
|||
.experimental-woocommerce-tree-item {
|
||||
margin: 0;
|
||||
|
||||
&__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;
|
||||
}
|
||||
}
|
||||
&__expander {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.components-button {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Button } 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 },
|
||||
} = useTreeItem( {
|
||||
...props,
|
||||
ref,
|
||||
} );
|
||||
|
||||
return (
|
||||
<li
|
||||
{ ...treeItemProps }
|
||||
className={ classNames(
|
||||
treeItemProps.className,
|
||||
'experimental-woocommerce-tree-item'
|
||||
) }
|
||||
>
|
||||
<div
|
||||
{ ...headingProps }
|
||||
className="experimental-woocommerce-tree-item__heading"
|
||||
>
|
||||
<div className="experimental-woocommerce-tree-item__label">
|
||||
<span>{ item.data.label }</span>
|
||||
</div>
|
||||
|
||||
{ Boolean( item.children?.length ) && (
|
||||
<div className="experimental-woocommerce-tree-item__expander">
|
||||
<Button
|
||||
icon={ isExpanded ? chevronUp : chevronDown }
|
||||
onClick={ onToggleExpand }
|
||||
className="experimental-woocommerce-tree-item__expander"
|
||||
aria-label={
|
||||
isExpanded
|
||||
? __( 'Collapse', 'woocommerce' )
|
||||
: __( 'Expand', 'woocommerce' )
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
|
||||
{ Boolean( item.children.length ) && isExpanded && (
|
||||
<Tree { ...treeProps } />
|
||||
) }
|
||||
</li>
|
||||
);
|
||||
} );
|
|
@ -0,0 +1,15 @@
|
|||
@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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import classNames from 'classnames';
|
||||
import { createElement, forwardRef } from 'react';
|
||||
|
||||
/**
|
||||
* 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,
|
||||
ref: React.ForwardedRef< HTMLOListElement >
|
||||
) {
|
||||
const { level, items, treeProps, treeItemProps } = useTree( {
|
||||
...props,
|
||||
ref,
|
||||
} );
|
||||
|
||||
if ( ! items.length ) return null;
|
||||
return (
|
||||
<ol
|
||||
{ ...treeProps }
|
||||
className={ classNames(
|
||||
treeProps.className,
|
||||
'experimental-woocommerce-tree',
|
||||
`experimental-woocommerce-tree--level-${ level }`
|
||||
) }
|
||||
>
|
||||
{ items.map( ( child ) => (
|
||||
<TreeItem
|
||||
{ ...treeItemProps }
|
||||
key={ child.data.value }
|
||||
item={ child }
|
||||
/>
|
||||
) ) }
|
||||
</ol>
|
||||
);
|
||||
} );
|
|
@ -0,0 +1,47 @@
|
|||
export interface Item {
|
||||
parent?: string;
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface LinkedTree {
|
||||
parent?: LinkedTree;
|
||||
data: Item;
|
||||
children: LinkedTree[];
|
||||
}
|
||||
|
||||
export type TreeProps = React.DetailedHTMLProps<
|
||||
React.OlHTMLAttributes< HTMLOListElement >,
|
||||
HTMLOListElement
|
||||
> & {
|
||||
level?: number;
|
||||
items: LinkedTree[];
|
||||
/**
|
||||
* Return if the tree item passed in should be expanded.
|
||||
*
|
||||
* @example
|
||||
* <Tree
|
||||
* shouldItemBeExpanded={
|
||||
* ( item ) => checkExpanded( item, filter )
|
||||
* }
|
||||
* />
|
||||
*
|
||||
* @param item The tree item to determine if should be expanded.
|
||||
*
|
||||
* @see {@link LinkedTree}
|
||||
*/
|
||||
shouldItemBeExpanded?( item: LinkedTree ): boolean;
|
||||
};
|
||||
|
||||
export type TreeItemProps = React.DetailedHTMLProps<
|
||||
React.LiHTMLAttributes< HTMLLIElement >,
|
||||
HTMLLIElement
|
||||
> & {
|
||||
level: number;
|
||||
item: LinkedTree;
|
||||
shouldItemBeExpanded?( item: LinkedTree ): boolean;
|
||||
};
|
||||
|
||||
export type TreeControlProps = Omit< TreeProps, 'items' | 'level' > & {
|
||||
items: Item[];
|
||||
};
|
|
@ -87,3 +87,17 @@ export { CollapsibleContent } from './collapsible-content';
|
|||
export { createOrderedChildren, sortFillsByOrder } from './utils';
|
||||
export { WooProductFieldItem as __experimentalWooProductFieldItem } from './woo-product-field-item';
|
||||
export { WooProductSectionItem as __experimentalWooProductSectionItem } from './woo-product-section-item';
|
||||
export { WooProductTabItem as __experimentalWooProductTabItem } from './woo-product-tab-item';
|
||||
export * from './product-fields';
|
||||
export {
|
||||
SlotContextProvider,
|
||||
useSlotContext,
|
||||
SlotContextType,
|
||||
SlotContextHelpersType,
|
||||
} from './slot-context';
|
||||
|
||||
// Exports below can be removed once the @woocommerce/product-editor package is released.
|
||||
export {
|
||||
ProductSectionLayout as __experimentalProductSectionLayout,
|
||||
ProductFieldSection as __experimentalProductFieldSection,
|
||||
} from './product-section-layout';
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
useState,
|
||||
useEffect,
|
||||
} from '@wordpress/element';
|
||||
import { SyntheticEvent } from 'react';
|
||||
import { SyntheticEvent, useCallback } from 'react';
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
import { PLUGINS_STORE_NAME, InstallPluginsResponse } from '@woocommerce/data';
|
||||
|
||||
|
@ -25,6 +25,11 @@ type PluginsProps = {
|
|||
pluginSlugs?: string[];
|
||||
onAbort?: () => void;
|
||||
abortText?: string;
|
||||
installText?: string;
|
||||
installButtonVariant?: Button.BaseProps[ 'variant' ];
|
||||
learnMoreLink?: string;
|
||||
learnMoreText?: string;
|
||||
onLearnMore?: () => void;
|
||||
};
|
||||
|
||||
export const Plugins = ( {
|
||||
|
@ -34,10 +39,17 @@ export const Plugins = ( {
|
|||
onError = () => null,
|
||||
pluginSlugs = [ 'jetpack', 'woocommerce-services' ],
|
||||
onSkip,
|
||||
installText = __( 'Install & enable', 'woocommerce' ),
|
||||
skipText = __( 'No thanks', 'woocommerce' ),
|
||||
abortText = __( 'Abort', 'woocommerce' ),
|
||||
installButtonVariant = 'primary',
|
||||
learnMoreLink,
|
||||
learnMoreText = __( 'Learn more', 'woocommerce' ),
|
||||
onLearnMore,
|
||||
}: PluginsProps ) => {
|
||||
const [ hasErrors, setHasErrors ] = useState( false );
|
||||
// Tracks action so that multiple instances of this button don't all light up when one is clicked
|
||||
const [ hasBeenClicked, setHasBeenClicked ] = useState( false );
|
||||
const { installAndActivatePlugins } = useDispatch( PLUGINS_STORE_NAME );
|
||||
const { isRequesting } = useSelect( ( select ) => {
|
||||
const { getActivePlugins, getInstalledPlugins, isPluginsRequesting } =
|
||||
|
@ -52,48 +64,56 @@ export const Plugins = ( {
|
|||
};
|
||||
} );
|
||||
|
||||
const handleErrors = (
|
||||
errors: unknown,
|
||||
response: InstallPluginsResponse
|
||||
) => {
|
||||
setHasErrors( true );
|
||||
const handleErrors = useCallback(
|
||||
( errors: unknown, response: InstallPluginsResponse ) => {
|
||||
setHasErrors( true );
|
||||
|
||||
onError( errors, response );
|
||||
};
|
||||
onError( errors, response );
|
||||
},
|
||||
[ onError ]
|
||||
);
|
||||
|
||||
const handleSuccess = (
|
||||
plugins: string[],
|
||||
response: InstallPluginsResponse
|
||||
) => {
|
||||
onComplete( plugins, response );
|
||||
};
|
||||
const handleSuccess = useCallback(
|
||||
( plugins: string[], response: InstallPluginsResponse ) => {
|
||||
onComplete( plugins, response );
|
||||
},
|
||||
[ onComplete ]
|
||||
);
|
||||
|
||||
const installAndActivate = async (
|
||||
event?: SyntheticEvent< HTMLAnchorElement >
|
||||
) => {
|
||||
if ( event ) {
|
||||
event.preventDefault();
|
||||
}
|
||||
const installAndActivate = useCallback(
|
||||
async ( event?: SyntheticEvent< HTMLAnchorElement > ) => {
|
||||
if ( event ) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// Avoid double activating.
|
||||
if ( isRequesting ) {
|
||||
return false;
|
||||
}
|
||||
// Avoid double activating.
|
||||
if ( isRequesting ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
installAndActivatePlugins( pluginSlugs )
|
||||
.then( ( response ) => {
|
||||
handleSuccess( response.data.activated, response );
|
||||
} )
|
||||
.catch( ( response ) => {
|
||||
handleErrors( response.errors, response );
|
||||
} );
|
||||
};
|
||||
installAndActivatePlugins( pluginSlugs )
|
||||
.then( ( response ) => {
|
||||
handleSuccess( response.data.activated, response );
|
||||
} )
|
||||
.catch( ( response ) => {
|
||||
setHasBeenClicked( false );
|
||||
handleErrors( response.errors, response );
|
||||
} );
|
||||
},
|
||||
[
|
||||
handleErrors,
|
||||
handleSuccess,
|
||||
installAndActivatePlugins,
|
||||
isRequesting,
|
||||
pluginSlugs,
|
||||
]
|
||||
);
|
||||
|
||||
useEffect( () => {
|
||||
if ( autoInstall ) {
|
||||
installAndActivate();
|
||||
}
|
||||
}, [] );
|
||||
}, [ autoInstall, installAndActivate ] );
|
||||
|
||||
if ( hasErrors ) {
|
||||
return (
|
||||
|
@ -131,17 +151,32 @@ export const Plugins = ( {
|
|||
return (
|
||||
<>
|
||||
<Button
|
||||
isBusy={ isRequesting }
|
||||
isPrimary
|
||||
onClick={ installAndActivate }
|
||||
isBusy={ isRequesting && hasBeenClicked }
|
||||
variant={
|
||||
isRequesting && hasBeenClicked
|
||||
? 'primary' // set to primary when busy, the other variants look weird when combined with isBusy
|
||||
: installButtonVariant
|
||||
}
|
||||
disabled={ isRequesting && hasBeenClicked }
|
||||
onClick={ () => {
|
||||
setHasBeenClicked( true );
|
||||
installAndActivate();
|
||||
} }
|
||||
>
|
||||
{ __( 'Install & enable', 'woocommerce' ) }
|
||||
{ installText }
|
||||
</Button>
|
||||
{ onSkip && (
|
||||
<Button isTertiary onClick={ onSkip }>
|
||||
{ skipText }
|
||||
</Button>
|
||||
) }
|
||||
{ learnMoreLink && (
|
||||
<a href={ learnMoreLink } target="_blank" rel="noreferrer">
|
||||
<Button isTertiary onClick={ onLearnMore }>
|
||||
{ learnMoreText }
|
||||
</Button>
|
||||
</a>
|
||||
) }
|
||||
{ onAbort && (
|
||||
<Button isTertiary onClick={ onAbort }>
|
||||
{ abortText }
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
*/
|
||||
import { select } from '@wordpress/data';
|
||||
import { createElement } from '@wordpress/element';
|
||||
import {
|
||||
// @ts-expect-error `__experimentalInputControl` does exist.
|
||||
__experimentalInputControl as InputControl,
|
||||
} from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -19,10 +23,7 @@ export function renderField( name: string, props: Record< string, any > ) {
|
|||
return <fieldConfig.render { ...props } />;
|
||||
}
|
||||
if ( fieldConfig.type ) {
|
||||
return createElement( 'input', {
|
||||
type: fieldConfig.type,
|
||||
...props,
|
||||
} );
|
||||
return <InputControl type={ fieldConfig.type } { ...props } />;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ProductFieldDefinition } from '../../store/types';
|
||||
import render from './render';
|
||||
|
||||
export const basicSelectControlSettings: ProductFieldDefinition = {
|
||||
name: 'basic-select-control',
|
||||
render: render as ComponentType,
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createElement } from '@wordpress/element';
|
||||
import { RadioControl, SelectControl } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { BaseProductFieldProps } from '../types';
|
||||
|
||||
type SelectControlFieldProps = BaseProductFieldProps< string | string[] > & {
|
||||
multiple?: boolean;
|
||||
options: SelectControl.Option[];
|
||||
};
|
||||
const SelectControlField: React.FC< SelectControlFieldProps > = ( {
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
multiple,
|
||||
options = [],
|
||||
} ) => {
|
||||
return (
|
||||
<SelectControl
|
||||
multiple={ multiple }
|
||||
label={ label }
|
||||
options={ options }
|
||||
onChange={ onChange }
|
||||
value={ value }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectControlField;
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ProductFieldDefinition } from '../../store/types';
|
||||
import render from './render';
|
||||
|
||||
export const checkboxSettings: ProductFieldDefinition = {
|
||||
name: 'checkbox',
|
||||
render: render as ComponentType,
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createElement } from '@wordpress/element';
|
||||
import { CheckboxControl } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { BaseProductFieldProps } from '../types';
|
||||
|
||||
type CheckboxFieldProps = BaseProductFieldProps< boolean >;
|
||||
|
||||
const CheckboxField: React.FC< CheckboxFieldProps > = ( {
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
} ) => {
|
||||
return (
|
||||
<CheckboxControl
|
||||
label={ label }
|
||||
onChange={ onChange }
|
||||
selected={ value }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckboxField;
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { registerProductField } from '../api';
|
||||
import { ProductFieldDefinition } from '../store/types';
|
||||
import { basicSelectControlSettings } from './basic-select-control';
|
||||
import { checkboxSettings } from './checkbox';
|
||||
import { radioSettings } from './radio';
|
||||
import { textSettings } from './text';
|
||||
import { toggleSettings } from './toggle';
|
||||
|
||||
const getAllProductFields = (): ProductFieldDefinition[] =>
|
||||
[
|
||||
...[ 'number' ].map( ( type ) => ( {
|
||||
name: type,
|
||||
type,
|
||||
} ) ),
|
||||
textSettings,
|
||||
toggleSettings,
|
||||
radioSettings,
|
||||
basicSelectControlSettings,
|
||||
checkboxSettings,
|
||||
].filter( Boolean );
|
||||
|
||||
export const registerCoreProductFields = ( fields = getAllProductFields() ) => {
|
||||
fields.forEach( ( field ) => {
|
||||
registerProductField( field.name, field );
|
||||
} );
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ProductFieldDefinition } from '../../store/types';
|
||||
import render from './render';
|
||||
|
||||
export const radioSettings: ProductFieldDefinition = {
|
||||
name: 'radio',
|
||||
render: render as ComponentType,
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createElement } from '@wordpress/element';
|
||||
import { RadioControl } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { BaseProductFieldProps } from '../types';
|
||||
|
||||
type RadioFieldProps = BaseProductFieldProps< string > & {
|
||||
options: {
|
||||
label: string;
|
||||
value: string;
|
||||
}[];
|
||||
};
|
||||
const RadioField: React.FC< RadioFieldProps > = ( {
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
options = [],
|
||||
} ) => {
|
||||
return (
|
||||
<RadioControl
|
||||
label={ label }
|
||||
options={ options }
|
||||
onChange={ onChange }
|
||||
selected={ value }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default RadioField;
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ProductFieldDefinition } from '../../store/types';
|
||||
import render from './render';
|
||||
|
||||
export const textSettings: ProductFieldDefinition = {
|
||||
name: 'text',
|
||||
render: render as ComponentType,
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createElement } from '@wordpress/element';
|
||||
import { TextControl } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { BaseProductFieldProps } from '../types';
|
||||
|
||||
type TextFieldProps = BaseProductFieldProps< string >;
|
||||
|
||||
const TextField: React.FC< TextFieldProps > = ( {
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
} ) => {
|
||||
return (
|
||||
<TextControl label={ label } onChange={ onChange } value={ value } />
|
||||
);
|
||||
};
|
||||
|
||||
export default TextField;
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ProductFieldDefinition } from '../../store/types';
|
||||
import render from './render';
|
||||
|
||||
export const toggleSettings: ProductFieldDefinition = {
|
||||
name: 'toggle',
|
||||
render: render as ComponentType,
|
||||
};
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createElement, Fragment } from '@wordpress/element';
|
||||
import { ToggleControl } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { BaseProductFieldProps } from '../types';
|
||||
import { Tooltip } from '../../../tooltip';
|
||||
|
||||
type ToggleFieldProps = BaseProductFieldProps< boolean > & {
|
||||
tooltip?: string;
|
||||
};
|
||||
const ToggleField: React.FC< ToggleFieldProps > = ( {
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
tooltip,
|
||||
disabled = false,
|
||||
} ) => {
|
||||
return (
|
||||
<ToggleControl
|
||||
label={
|
||||
<>
|
||||
{ label }
|
||||
{ tooltip && <Tooltip text={ tooltip } /> }
|
||||
</>
|
||||
}
|
||||
checked={ value }
|
||||
onChange={ onChange }
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore disabled prop exists
|
||||
disabled={ disabled }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToggleField;
|
|
@ -0,0 +1,6 @@
|
|||
export type BaseProductFieldProps< T > = {
|
||||
value: T;
|
||||
onChange: ( value: T ) => void;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
};
|
|
@ -1,2 +1,3 @@
|
|||
export { store } from './store';
|
||||
export * from './api';
|
||||
export * from './fields';
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { ComponentType } from 'react';
|
||||
import { ComponentType, HTMLInputTypeAttribute } from 'react';
|
||||
|
||||
export type ProductFieldDefinition = {
|
||||
name: string;
|
||||
type?: string;
|
||||
type?: HTMLInputTypeAttribute;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
render?: ComponentType;
|
||||
};
|
||||
|
|
|
@ -3,56 +3,92 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { useState, createElement } from '@wordpress/element';
|
||||
import { createRegistry, RegistryProvider, select } from '@wordpress/data';
|
||||
import {
|
||||
// @ts-expect-error `__experimentalInputControl` does exist.
|
||||
__experimentalInputControl as InputControl,
|
||||
} from '@wordpress/components';
|
||||
import { createRegistry, RegistryProvider } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { store } from '../store';
|
||||
import { registerProductField, renderField } from '../api';
|
||||
import { renderField } from '../api';
|
||||
import { registerCoreProductFields } from '../fields';
|
||||
|
||||
const registry = createRegistry();
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore No types for this exist yet.
|
||||
registry.register( store );
|
||||
|
||||
registerProductField( 'text', {
|
||||
name: 'text',
|
||||
render: ( props ) => {
|
||||
return <InputControl type="text" { ...props } />;
|
||||
},
|
||||
} );
|
||||
registerCoreProductFields();
|
||||
|
||||
registerProductField( 'number', {
|
||||
name: 'number',
|
||||
render: () => {
|
||||
return <InputControl type="number" />;
|
||||
const fieldConfigs = [
|
||||
{
|
||||
name: 'text-field',
|
||||
type: 'text',
|
||||
label: 'Text field',
|
||||
},
|
||||
} );
|
||||
{
|
||||
name: 'number-field',
|
||||
type: 'number',
|
||||
label: 'Number field',
|
||||
},
|
||||
{
|
||||
name: 'toggle-field',
|
||||
type: 'toggle',
|
||||
label: 'Toggle field',
|
||||
},
|
||||
{
|
||||
name: 'checkbox-field',
|
||||
type: 'checkbox',
|
||||
label: 'Checkbox field',
|
||||
},
|
||||
{
|
||||
name: 'radio-field',
|
||||
type: 'radio',
|
||||
label: 'Radio field',
|
||||
options: [
|
||||
{ label: 'Option', value: 'option' },
|
||||
{ label: 'Option 2', value: 'option2' },
|
||||
{ label: 'Option 3', value: 'option3' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'basic-select-control-field',
|
||||
type: 'basic-select-control',
|
||||
label: 'Basic select control field',
|
||||
options: [
|
||||
{ label: 'Option', value: 'option' },
|
||||
{ label: 'Option 2', value: 'option2' },
|
||||
{ label: 'Option 3', value: 'option3' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const RenderField = () => {
|
||||
const fields: string[] = select( store ).getRegisteredProductFields();
|
||||
const [ selectedField, setSelectedField ] = useState(
|
||||
fields ? fields[ 0 ] : undefined
|
||||
fieldConfigs[ 0 ].name || undefined
|
||||
);
|
||||
const [ value, setValue ] = useState();
|
||||
|
||||
const handleChange = ( event ) => {
|
||||
setSelectedField( event.target.value );
|
||||
};
|
||||
const selectedFieldConfig = fieldConfigs.find(
|
||||
( f ) => f.name === selectedField
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<select value={ selectedField } onChange={ handleChange }>
|
||||
{ fields.map( ( field ) => (
|
||||
<option key={ field } value={ field }>
|
||||
{ field }
|
||||
{ fieldConfigs.map( ( field ) => (
|
||||
<option key={ field.name } value={ field.name }>
|
||||
{ field.label }
|
||||
</option>
|
||||
) ) }
|
||||
</select>
|
||||
{ selectedField && renderField( selectedField, { name: 'test' } ) }
|
||||
{ selectedFieldConfig &&
|
||||
renderField( selectedFieldConfig.type, {
|
||||
value,
|
||||
onChange: setValue,
|
||||
...selectedFieldConfig,
|
||||
} ) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -65,6 +101,21 @@ export const Basic: React.FC = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export const ToggleWithTooltip: React.FC = () => {
|
||||
const [ value, setValue ] = useState();
|
||||
return (
|
||||
<RegistryProvider value={ registry }>
|
||||
{ renderField( 'toggle', {
|
||||
value,
|
||||
onChange: setValue,
|
||||
name: 'toggle',
|
||||
label: 'Toggle with Tooltip',
|
||||
tooltip: 'This is a sample tooltip',
|
||||
} ) }
|
||||
</RegistryProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Admin/experimental/product-fields',
|
||||
component: Basic,
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
export * from './product-section-layout';
|
||||
export * from './product-field-section';
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createElement } from '@wordpress/element';
|
||||
import { Card, CardBody } from '@wordpress/components';
|
||||
import deprecated from '@wordpress/deprecated';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ProductSectionLayout } from './product-section-layout';
|
||||
import { WooProductFieldItem } from '../woo-product-field-item';
|
||||
|
||||
type ProductFieldSectionProps = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string | JSX.Element;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const ProductFieldSection: React.FC< ProductFieldSectionProps > = ( {
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
className,
|
||||
children,
|
||||
} ) => {
|
||||
deprecated( `__experimentalProductFieldSection`, {
|
||||
version: '13.0.0',
|
||||
plugin: '@woocommerce/components',
|
||||
hint: 'Moved to @woocommerce/product-editor package: import { __experimentalProductFieldSection } from @woocommerce/product-editor',
|
||||
} );
|
||||
return (
|
||||
<ProductSectionLayout
|
||||
title={ title }
|
||||
description={ description }
|
||||
className={ className }
|
||||
>
|
||||
<Card>
|
||||
<CardBody>
|
||||
{ children }
|
||||
<WooProductFieldItem.Slot section={ id } />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</ProductSectionLayout>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Children, isValidElement, createElement } from '@wordpress/element';
|
||||
import deprecated from '@wordpress/deprecated';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { FormSection } from '../form-section';
|
||||
|
||||
type ProductSectionLayoutProps = {
|
||||
title: string;
|
||||
description: string | JSX.Element;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const ProductSectionLayout: React.FC< ProductSectionLayoutProps > = ( {
|
||||
title,
|
||||
description,
|
||||
className,
|
||||
children,
|
||||
} ) => {
|
||||
deprecated( `__experimentalProductSectionLayout`, {
|
||||
version: '13.0.0',
|
||||
plugin: '@woocommerce/components',
|
||||
hint: 'Moved to @woocommerce/product-editor package: import { __experimentalProductSectionLayout } from @woocommerce/product-editor',
|
||||
} );
|
||||
return (
|
||||
<FormSection
|
||||
title={ title }
|
||||
description={ description }
|
||||
className={ className }
|
||||
>
|
||||
{ Children.map( children, ( child ) => {
|
||||
if ( isValidElement( child ) && child.props.onChange ) {
|
||||
return (
|
||||
<div className="product-field-layout">{ child }</div>
|
||||
);
|
||||
}
|
||||
return child;
|
||||
} ) }
|
||||
</FormSection>
|
||||
);
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from './slot-context';
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import {
|
||||
createElement,
|
||||
createContext,
|
||||
useContext,
|
||||
useCallback,
|
||||
useReducer,
|
||||
} from '@wordpress/element';
|
||||
|
||||
type FillConfigType = {
|
||||
visible: boolean;
|
||||
};
|
||||
|
||||
type FillType = Record< string, FillConfigType >;
|
||||
|
||||
type FillCollection = readonly ( readonly JSX.Element[] )[];
|
||||
|
||||
export type SlotContextHelpersType = {
|
||||
hideFill: ( id: string ) => void;
|
||||
showFill: ( id: string ) => void;
|
||||
getFills: () => FillType;
|
||||
};
|
||||
|
||||
export type SlotContextType = {
|
||||
fills: FillType;
|
||||
getFillHelpers: () => SlotContextHelpersType;
|
||||
registerFill: ( id: string ) => void;
|
||||
filterRegisteredFills: ( fillsArrays: FillCollection ) => FillCollection;
|
||||
};
|
||||
|
||||
const SlotContext = createContext< SlotContextType | undefined >( undefined );
|
||||
|
||||
export const SlotContextProvider: React.FC = ( { children } ) => {
|
||||
const [ fills, updateFills ] = useReducer(
|
||||
( data: FillType, updates: FillType ) => ( { ...data, ...updates } ),
|
||||
{}
|
||||
);
|
||||
|
||||
const updateFillConfig = (
|
||||
id: string,
|
||||
update: Partial< FillConfigType >
|
||||
) => {
|
||||
if ( ! fills[ id ] ) {
|
||||
throw new Error( `No fill found with ID: ${ id }` );
|
||||
}
|
||||
updateFills( { [ id ]: { ...fills[ id ], ...update } } );
|
||||
};
|
||||
|
||||
const registerFill = useCallback(
|
||||
( id: string ) => {
|
||||
if ( fills[ id ] ) {
|
||||
return;
|
||||
}
|
||||
updateFills( { [ id ]: { visible: true } } );
|
||||
},
|
||||
[ fills ]
|
||||
);
|
||||
|
||||
const hideFill = useCallback(
|
||||
( id: string ) => updateFillConfig( id, { visible: false } ),
|
||||
[ fills ]
|
||||
);
|
||||
|
||||
const showFill = useCallback(
|
||||
( id: string ) => updateFillConfig( id, { visible: true } ),
|
||||
[ fills ]
|
||||
);
|
||||
|
||||
const getFills = useCallback( () => ( { ...fills } ), [ fills ] );
|
||||
|
||||
return (
|
||||
<SlotContext.Provider
|
||||
value={ {
|
||||
registerFill,
|
||||
getFillHelpers() {
|
||||
return { hideFill, showFill, getFills };
|
||||
},
|
||||
filterRegisteredFills( fillsArrays: FillCollection ) {
|
||||
return fillsArrays.filter(
|
||||
( arr ) =>
|
||||
fills[ arr[ 0 ].props._id ]?.visible !== false
|
||||
);
|
||||
},
|
||||
fills,
|
||||
} }
|
||||
>
|
||||
{ children }
|
||||
</SlotContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useSlotContext = () => {
|
||||
const slotContext = useContext( SlotContext );
|
||||
|
||||
if ( slotContext === undefined ) {
|
||||
throw new Error(
|
||||
'useSlotContext must be used within a SlotContextProvider'
|
||||
);
|
||||
}
|
||||
|
||||
return slotContext;
|
||||
};
|
|
@ -55,3 +55,5 @@
|
|||
@import 'tour-kit/style.scss';
|
||||
@import 'collapsible-content/style.scss';
|
||||
@import 'form/style.scss';
|
||||
@import 'experimental-tree-control/tree.scss';
|
||||
@import 'product-section-layout/style.scss';
|
||||
|
|
|
@ -1,39 +1,32 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import PropTypes from 'prop-types';
|
||||
import { createElement } from '@wordpress/element';
|
||||
import React from 'react';
|
||||
|
||||
type EmptyTableProps = {
|
||||
children: React.ReactNode;
|
||||
|
||||
/** An integer with the number of rows the box should occupy. */
|
||||
numberOfRows?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* `EmptyTable` displays a blank space with an optional message passed as a children node
|
||||
* with the purpose of replacing a table with no rows.
|
||||
* It mimics the same height a table would have according to the `numberOfRows` prop.
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {Node} props.children
|
||||
* @param {number} props.numberOfRows
|
||||
* @return {Object} -
|
||||
*/
|
||||
const EmptyTable = ( { children, numberOfRows } ) => {
|
||||
const EmptyTable = ( { children, numberOfRows = 5 }: EmptyTableProps ) => {
|
||||
return (
|
||||
<div
|
||||
className="woocommerce-table is-empty"
|
||||
style={ { '--number-of-rows': numberOfRows } }
|
||||
style={
|
||||
{ '--number-of-rows': numberOfRows } as React.CSSProperties
|
||||
}
|
||||
>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
EmptyTable.propTypes = {
|
||||
/**
|
||||
* An integer with the number of rows the box should occupy.
|
||||
*/
|
||||
numberOfRows: PropTypes.number,
|
||||
};
|
||||
|
||||
EmptyTable.defaultProps = {
|
||||
numberOfRows: 5,
|
||||
};
|
||||
|
||||
export default EmptyTable;
|
|
@ -1,384 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import classnames from 'classnames';
|
||||
import {
|
||||
Card,
|
||||
CardBody,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
__experimentalText as Text,
|
||||
} from '@wordpress/components';
|
||||
import { createElement, Component, Fragment } from '@wordpress/element';
|
||||
import { find, first, isEqual, without } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import EllipsisMenu from '../ellipsis-menu';
|
||||
import MenuItem from '../ellipsis-menu/menu-item';
|
||||
import MenuTitle from '../ellipsis-menu/menu-title';
|
||||
import Pagination from '../pagination';
|
||||
import Table from './table';
|
||||
import TablePlaceholder from './placeholder';
|
||||
import TableSummary, { TableSummaryPlaceholder } from './summary';
|
||||
|
||||
/**
|
||||
* This is an accessible, sortable, and scrollable table for displaying tabular data (like revenue and other analytics data).
|
||||
* It accepts `headers` for column headers, and `rows` for the table content.
|
||||
* `rowHeader` can be used to define the index of the row header (or false if no header).
|
||||
*
|
||||
* `TableCard` serves as Card wrapper & contains a card header, `<Table />`, `<TableSummary />`, and `<Pagination />`.
|
||||
* This includes filtering and comparison functionality for report pages.
|
||||
*/
|
||||
class TableCard extends Component {
|
||||
constructor( props ) {
|
||||
super( props );
|
||||
const showCols = this.getShowCols( props.headers );
|
||||
|
||||
this.state = { showCols };
|
||||
this.onColumnToggle = this.onColumnToggle.bind( this );
|
||||
this.onPageChange = this.onPageChange.bind( this );
|
||||
}
|
||||
|
||||
componentDidUpdate( { headers: prevHeaders, query: prevQuery } ) {
|
||||
const { headers, onColumnsChange, query } = this.props;
|
||||
const { showCols } = this.state;
|
||||
|
||||
if ( ! isEqual( headers, prevHeaders ) ) {
|
||||
/* eslint-disable react/no-did-update-set-state */
|
||||
this.setState( {
|
||||
showCols: this.getShowCols( headers ),
|
||||
} );
|
||||
/* eslint-enable react/no-did-update-set-state */
|
||||
}
|
||||
if (
|
||||
query.orderby !== prevQuery.orderby &&
|
||||
! showCols.includes( query.orderby )
|
||||
) {
|
||||
const newShowCols = showCols.concat( query.orderby );
|
||||
/* eslint-disable react/no-did-update-set-state */
|
||||
this.setState( {
|
||||
showCols: newShowCols,
|
||||
} );
|
||||
/* eslint-enable react/no-did-update-set-state */
|
||||
onColumnsChange( newShowCols );
|
||||
}
|
||||
}
|
||||
|
||||
getShowCols( headers ) {
|
||||
return headers
|
||||
.map( ( { key, visible } ) => {
|
||||
if ( typeof visible === 'undefined' || visible ) {
|
||||
return key;
|
||||
}
|
||||
return false;
|
||||
} )
|
||||
.filter( Boolean );
|
||||
}
|
||||
|
||||
getVisibleHeaders() {
|
||||
const { headers } = this.props;
|
||||
const { showCols } = this.state;
|
||||
return headers.filter( ( { key } ) => showCols.includes( key ) );
|
||||
}
|
||||
|
||||
getVisibleRows() {
|
||||
const { headers, rows } = this.props;
|
||||
const { showCols } = this.state;
|
||||
|
||||
return rows.map( ( row ) => {
|
||||
return headers
|
||||
.map( ( { key }, i ) => {
|
||||
return showCols.includes( key ) && row[ i ];
|
||||
} )
|
||||
.filter( Boolean );
|
||||
} );
|
||||
}
|
||||
|
||||
onColumnToggle( key ) {
|
||||
const { headers, query, onQueryChange, onColumnsChange } = this.props;
|
||||
|
||||
return () => {
|
||||
this.setState( ( prevState ) => {
|
||||
const hasKey = prevState.showCols.includes( key );
|
||||
|
||||
if ( hasKey ) {
|
||||
// Handle hiding a sorted column
|
||||
if ( query.orderby === key ) {
|
||||
const defaultSort =
|
||||
find( headers, { defaultSort: true } ) ||
|
||||
first( headers ) ||
|
||||
{};
|
||||
onQueryChange( 'sort' )( defaultSort.key, 'desc' );
|
||||
}
|
||||
|
||||
const showCols = without( prevState.showCols, key );
|
||||
onColumnsChange( showCols, key );
|
||||
return { showCols };
|
||||
}
|
||||
|
||||
const showCols = [ ...prevState.showCols, key ];
|
||||
onColumnsChange( showCols, key );
|
||||
return { showCols };
|
||||
} );
|
||||
};
|
||||
}
|
||||
|
||||
onPageChange( ...params ) {
|
||||
const { onPageChange, onQueryChange } = this.props;
|
||||
if ( onPageChange ) {
|
||||
onPageChange( ...params );
|
||||
}
|
||||
if ( onQueryChange ) {
|
||||
onQueryChange( 'paged' )( ...params );
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
actions,
|
||||
className,
|
||||
hasSearch,
|
||||
isLoading,
|
||||
onQueryChange,
|
||||
onSort,
|
||||
query,
|
||||
rowHeader,
|
||||
rowsPerPage,
|
||||
showMenu,
|
||||
summary,
|
||||
title,
|
||||
totalRows,
|
||||
rowKey,
|
||||
emptyMessage,
|
||||
} = this.props;
|
||||
const { showCols } = this.state;
|
||||
const allHeaders = this.props.headers;
|
||||
const headers = this.getVisibleHeaders();
|
||||
const rows = this.getVisibleRows();
|
||||
const classes = classnames( 'woocommerce-table', className, {
|
||||
'has-actions': !! actions,
|
||||
'has-menu': showMenu,
|
||||
'has-search': hasSearch,
|
||||
} );
|
||||
|
||||
return (
|
||||
<Card className={ classes }>
|
||||
<CardHeader>
|
||||
<Text size={ 16 } weight={ 600 } as="h2" color="#23282d">
|
||||
{ title }
|
||||
</Text>
|
||||
<div className="woocommerce-table__actions">
|
||||
{ actions }
|
||||
</div>
|
||||
{ showMenu && (
|
||||
<EllipsisMenu
|
||||
label={ __(
|
||||
'Choose which values to display',
|
||||
'woocommerce'
|
||||
) }
|
||||
renderContent={ () => (
|
||||
<Fragment>
|
||||
<MenuTitle>
|
||||
{ __( 'Columns:', 'woocommerce' ) }
|
||||
</MenuTitle>
|
||||
{ allHeaders.map(
|
||||
( { key, label, required } ) => {
|
||||
if ( required ) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<MenuItem
|
||||
checked={ showCols.includes(
|
||||
key
|
||||
) }
|
||||
isCheckbox
|
||||
isClickable
|
||||
key={ key }
|
||||
onInvoke={ this.onColumnToggle(
|
||||
key
|
||||
) }
|
||||
>
|
||||
{ label }
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
) }
|
||||
</Fragment>
|
||||
) }
|
||||
/>
|
||||
) }
|
||||
</CardHeader>
|
||||
<CardBody size={ null }>
|
||||
{ isLoading ? (
|
||||
<Fragment>
|
||||
<span className="screen-reader-text">
|
||||
{ __(
|
||||
'Your requested data is loading',
|
||||
'woocommerce'
|
||||
) }
|
||||
</span>
|
||||
<TablePlaceholder
|
||||
numberOfRows={ rowsPerPage }
|
||||
headers={ headers }
|
||||
rowHeader={ rowHeader }
|
||||
caption={ title }
|
||||
query={ query }
|
||||
/>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Table
|
||||
rows={ rows }
|
||||
headers={ headers }
|
||||
rowHeader={ rowHeader }
|
||||
caption={ title }
|
||||
query={ query }
|
||||
onSort={ onSort || onQueryChange( 'sort' ) }
|
||||
rowKey={ rowKey }
|
||||
emptyMessage={ emptyMessage }
|
||||
/>
|
||||
) }
|
||||
</CardBody>
|
||||
|
||||
<CardFooter justify="center">
|
||||
{ isLoading ? (
|
||||
<TableSummaryPlaceholder />
|
||||
) : (
|
||||
<Fragment>
|
||||
<Pagination
|
||||
key={ parseInt( query.paged, 10 ) || 1 }
|
||||
page={ parseInt( query.paged, 10 ) || 1 }
|
||||
perPage={ rowsPerPage }
|
||||
total={ totalRows }
|
||||
onPageChange={ this.onPageChange }
|
||||
onPerPageChange={ onQueryChange( 'per_page' ) }
|
||||
/>
|
||||
|
||||
{ summary && <TableSummary data={ summary } /> }
|
||||
</Fragment>
|
||||
) }
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TableCard.propTypes = {
|
||||
/**
|
||||
* If a search is provided in actions and should reorder actions on mobile.
|
||||
*/
|
||||
hasSearch: PropTypes.bool,
|
||||
/**
|
||||
* An array of column headers (see `Table` props).
|
||||
*/
|
||||
headers: PropTypes.arrayOf(
|
||||
PropTypes.shape( {
|
||||
hiddenByDefault: PropTypes.bool,
|
||||
defaultSort: PropTypes.bool,
|
||||
isSortable: PropTypes.bool,
|
||||
key: PropTypes.string,
|
||||
label: PropTypes.oneOfType( [ PropTypes.string, PropTypes.node ] ),
|
||||
required: PropTypes.bool,
|
||||
} )
|
||||
),
|
||||
/**
|
||||
* A list of IDs, matching to the row list so that ids[ 0 ] contains the object ID for the object displayed in row[ 0 ].
|
||||
*/
|
||||
ids: PropTypes.arrayOf( PropTypes.number ),
|
||||
/**
|
||||
* Defines if the table contents are loading.
|
||||
* It will display `TablePlaceholder` component instead of `Table` if that's the case.
|
||||
*/
|
||||
isLoading: PropTypes.bool,
|
||||
/**
|
||||
* A function which returns a callback function to update the query string for a given `param`.
|
||||
*/
|
||||
onQueryChange: PropTypes.func,
|
||||
/**
|
||||
* A function which returns a callback function which is called upon the user changing the visiblity of columns.
|
||||
*/
|
||||
onColumnsChange: PropTypes.func,
|
||||
/**
|
||||
* A function which is called upon the user changing the sorting of the table.
|
||||
*/
|
||||
onSort: PropTypes.func,
|
||||
/**
|
||||
* An object of the query parameters passed to the page, ex `{ page: 2, per_page: 5 }`.
|
||||
*/
|
||||
query: PropTypes.object,
|
||||
/**
|
||||
* Which column should be the row header, defaults to the first item (`0`) (but could be set to `1`, if the first col
|
||||
* is checkboxes, for example). Set to false to disable row headers.
|
||||
*/
|
||||
rowHeader: PropTypes.oneOfType( [ PropTypes.number, PropTypes.bool ] ),
|
||||
/**
|
||||
* An array of arrays of display/value object pairs (see `Table` props).
|
||||
*/
|
||||
rows: PropTypes.arrayOf(
|
||||
PropTypes.arrayOf(
|
||||
PropTypes.shape( {
|
||||
display: PropTypes.node,
|
||||
value: PropTypes.oneOfType( [
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
PropTypes.bool,
|
||||
] ),
|
||||
} )
|
||||
)
|
||||
).isRequired,
|
||||
/**
|
||||
* The total number of rows to display per page.
|
||||
*/
|
||||
rowsPerPage: PropTypes.number.isRequired,
|
||||
/**
|
||||
* Boolean to determine whether or not ellipsis menu is shown.
|
||||
*/
|
||||
showMenu: PropTypes.bool,
|
||||
/**
|
||||
* An array of objects with `label` & `value` properties, which display in a line under the table.
|
||||
* Optional, can be left off to show no summary.
|
||||
*/
|
||||
summary: PropTypes.arrayOf(
|
||||
PropTypes.shape( {
|
||||
label: PropTypes.node,
|
||||
value: PropTypes.oneOfType( [
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
] ),
|
||||
} )
|
||||
),
|
||||
/**
|
||||
* The title used in the card header, also used as the caption for the content in this table.
|
||||
*/
|
||||
title: PropTypes.string.isRequired,
|
||||
/**
|
||||
* The total number of rows (across all pages).
|
||||
*/
|
||||
totalRows: PropTypes.number.isRequired,
|
||||
/**
|
||||
* The rowKey used for the key value on each row, this can be a string of the key or a function that returns the value.
|
||||
* This uses the index if not defined.
|
||||
*/
|
||||
rowKey: PropTypes.func,
|
||||
/**
|
||||
* Customize the message to show when there are no rows in the table.
|
||||
*/
|
||||
emptyMessage: PropTypes.string,
|
||||
};
|
||||
|
||||
TableCard.defaultProps = {
|
||||
isLoading: false,
|
||||
onQueryChange: () => () => {},
|
||||
onColumnsChange: () => {},
|
||||
onSort: undefined,
|
||||
query: {},
|
||||
rowHeader: 0,
|
||||
rows: [],
|
||||
showMenu: true,
|
||||
emptyMessage: undefined,
|
||||
};
|
||||
|
||||
export default TableCard;
|
|
@ -0,0 +1,248 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import classnames from 'classnames';
|
||||
import { createElement, Fragment, useState } from '@wordpress/element';
|
||||
import { find, first, without } from 'lodash';
|
||||
import React from 'react';
|
||||
import {
|
||||
Card,
|
||||
CardBody,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
// @ts-expect-error: Suppressing Module '"@wordpress/components"' has no exported member '__experimentalText'
|
||||
__experimentalText as Text,
|
||||
} from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import EllipsisMenu from '../ellipsis-menu';
|
||||
import MenuItem from '../ellipsis-menu/menu-item';
|
||||
import MenuTitle from '../ellipsis-menu/menu-title';
|
||||
import Pagination from '../pagination';
|
||||
import Table from './table';
|
||||
import TablePlaceholder from './placeholder';
|
||||
import TableSummary, { TableSummaryPlaceholder } from './summary';
|
||||
import { TableCardProps } from './types';
|
||||
|
||||
const defaultOnQueryChange =
|
||||
( param: string ) => ( path?: string, direction?: string ) => {};
|
||||
|
||||
const defaultOnColumnsChange = (
|
||||
showCols: Array< string >,
|
||||
key?: string
|
||||
) => {};
|
||||
/**
|
||||
* This is an accessible, sortable, and scrollable table for displaying tabular data (like revenue and other analytics data).
|
||||
* It accepts `headers` for column headers, and `rows` for the table content.
|
||||
* `rowHeader` can be used to define the index of the row header (or false if no header).
|
||||
*
|
||||
* `TableCard` serves as Card wrapper & contains a card header, `<Table />`, `<TableSummary />`, and `<Pagination />`.
|
||||
* This includes filtering and comparison functionality for report pages.
|
||||
*/
|
||||
const TableCard: React.VFC< TableCardProps > = ( {
|
||||
actions,
|
||||
className,
|
||||
hasSearch,
|
||||
headers = [],
|
||||
ids,
|
||||
isLoading = false,
|
||||
onQueryChange = defaultOnQueryChange,
|
||||
onColumnsChange = defaultOnColumnsChange,
|
||||
onSort,
|
||||
query = {},
|
||||
rowHeader = 0,
|
||||
rows = [],
|
||||
rowsPerPage,
|
||||
showMenu = true,
|
||||
summary,
|
||||
title,
|
||||
totalRows,
|
||||
rowKey,
|
||||
emptyMessage = undefined,
|
||||
...props
|
||||
} ) => {
|
||||
// eslint-disable-next-line no-console
|
||||
const getShowCols = ( _headers: TableCardProps[ 'headers' ] = [] ) => {
|
||||
return _headers
|
||||
.map( ( { key, visible } ) => {
|
||||
if ( typeof visible === 'undefined' || visible ) {
|
||||
return key;
|
||||
}
|
||||
return false;
|
||||
} )
|
||||
.filter( Boolean ) as string[];
|
||||
};
|
||||
|
||||
const [ showCols, setShowCols ] = useState( getShowCols( headers ) );
|
||||
|
||||
const onColumnToggle = ( key: string ) => {
|
||||
return () => {
|
||||
const hasKey = showCols.includes( key );
|
||||
|
||||
if ( hasKey ) {
|
||||
// Handle hiding a sorted column
|
||||
if ( query.orderby === key ) {
|
||||
const defaultSort = find( headers, {
|
||||
defaultSort: true,
|
||||
} ) ||
|
||||
first( headers ) || { key: undefined };
|
||||
onQueryChange( 'sort' )( defaultSort.key, 'desc' );
|
||||
}
|
||||
|
||||
const newShowCols = without( showCols, key );
|
||||
onColumnsChange( newShowCols, key );
|
||||
setShowCols( newShowCols );
|
||||
} else {
|
||||
const newShowCols = [ ...showCols, key ] as string[];
|
||||
onColumnsChange( newShowCols, key );
|
||||
setShowCols( newShowCols );
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const onPageChange = (
|
||||
newPage: string,
|
||||
direction?: 'previous' | 'next'
|
||||
) => {
|
||||
if ( props.onPageChange ) {
|
||||
props.onPageChange( parseInt( newPage, 10 ), direction );
|
||||
}
|
||||
if ( onQueryChange ) {
|
||||
onQueryChange( 'paged' )( newPage, direction );
|
||||
}
|
||||
};
|
||||
|
||||
const allHeaders = headers;
|
||||
const visibleHeaders = headers.filter( ( { key } ) =>
|
||||
showCols.includes( key )
|
||||
);
|
||||
const visibleRows = rows.map( ( row ) => {
|
||||
return headers
|
||||
.map( ( { key }, i ) => {
|
||||
return showCols.includes( key ) && row[ i ];
|
||||
} )
|
||||
.filter( Boolean );
|
||||
} );
|
||||
const classes = classnames( 'woocommerce-table', className, {
|
||||
'has-actions': !! actions,
|
||||
'has-menu': showMenu,
|
||||
'has-search': hasSearch,
|
||||
} );
|
||||
|
||||
return (
|
||||
<Card className={ classes }>
|
||||
<CardHeader>
|
||||
<Text size={ 16 } weight={ 600 } as="h2" color="#23282d">
|
||||
{ title }
|
||||
</Text>
|
||||
<div className="woocommerce-table__actions">{ actions }</div>
|
||||
{ showMenu && (
|
||||
<EllipsisMenu
|
||||
label={ __(
|
||||
'Choose which values to display',
|
||||
'woocommerce'
|
||||
) }
|
||||
renderContent={ () => (
|
||||
<Fragment>
|
||||
{ /* @ts-expect-error: Ignoring the error until we migrate ellipsis-menu to TS*/ }
|
||||
<MenuTitle>
|
||||
{ /* @ts-expect-error: Allow string */ }
|
||||
{ __( 'Columns:', 'woocommerce' ) }
|
||||
</MenuTitle>
|
||||
{ allHeaders.map(
|
||||
( { key, label, required } ) => {
|
||||
if ( required ) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<MenuItem
|
||||
checked={ showCols.includes(
|
||||
key
|
||||
) }
|
||||
isCheckbox
|
||||
isClickable
|
||||
key={ key }
|
||||
onInvoke={
|
||||
key !== undefined
|
||||
? onColumnToggle( key )
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{ label }
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
) }
|
||||
</Fragment>
|
||||
) }
|
||||
/>
|
||||
) }
|
||||
</CardHeader>
|
||||
{ /* Ignoring the error to make it backward compatible for now. */ }
|
||||
{ /* @ts-expect-error: size must be one of small, medium, largel, xSmall, extraSmall. */ }
|
||||
<CardBody size={ null }>
|
||||
{ isLoading ? (
|
||||
<Fragment>
|
||||
<span className="screen-reader-text">
|
||||
{ __(
|
||||
'Your requested data is loading',
|
||||
'woocommerce'
|
||||
) }
|
||||
</span>
|
||||
<TablePlaceholder
|
||||
numberOfRows={ rowsPerPage }
|
||||
headers={ visibleHeaders }
|
||||
rowHeader={ rowHeader }
|
||||
caption={ title }
|
||||
query={ query }
|
||||
/>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Table
|
||||
rows={ visibleRows as TableCardProps[ 'rows' ] }
|
||||
headers={
|
||||
visibleHeaders as TableCardProps[ 'headers' ]
|
||||
}
|
||||
rowHeader={ rowHeader }
|
||||
caption={ title }
|
||||
query={ query }
|
||||
onSort={
|
||||
onSort ||
|
||||
( onQueryChange( 'sort' ) as (
|
||||
key: string,
|
||||
direction: string
|
||||
) => void )
|
||||
}
|
||||
rowKey={ rowKey }
|
||||
emptyMessage={ emptyMessage }
|
||||
/>
|
||||
) }
|
||||
</CardBody>
|
||||
|
||||
{ /* @ts-expect-error: justify is missing from the latest @types/wordpress__components */ }
|
||||
<CardFooter justify="center">
|
||||
{ isLoading ? (
|
||||
<TableSummaryPlaceholder />
|
||||
) : (
|
||||
<Fragment>
|
||||
<Pagination
|
||||
key={ parseInt( query.paged as string, 10 ) || 1 }
|
||||
page={ parseInt( query.paged as string, 10 ) || 1 }
|
||||
perPage={ rowsPerPage }
|
||||
total={ totalRows }
|
||||
onPageChange={ onPageChange }
|
||||
onPerPageChange={ onQueryChange( 'per_page' ) }
|
||||
/>
|
||||
|
||||
{ summary && <TableSummary data={ summary } /> }
|
||||
</Fragment>
|
||||
) }
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default TableCard;
|
|
@ -1,68 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createElement, Component } from '@wordpress/element';
|
||||
import { range } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Table from './table';
|
||||
|
||||
/**
|
||||
* `TablePlaceholder` behaves like `Table` but displays placeholder boxes instead of data. This can be used while loading.
|
||||
*/
|
||||
class TablePlaceholder extends Component {
|
||||
render() {
|
||||
const { numberOfRows, ...tableProps } = this.props;
|
||||
const rows = range( numberOfRows ).map( () =>
|
||||
this.props.headers.map( () => ( {
|
||||
display: <span className="is-placeholder" />,
|
||||
} ) )
|
||||
);
|
||||
|
||||
return (
|
||||
<Table
|
||||
ariaHidden={ true }
|
||||
className="is-loading"
|
||||
rows={ rows }
|
||||
{ ...tableProps }
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TablePlaceholder.propTypes = {
|
||||
/**
|
||||
* An object of the query parameters passed to the page, ex `{ page: 2, per_page: 5 }`.
|
||||
*/
|
||||
query: PropTypes.object,
|
||||
/**
|
||||
* A label for the content in this table.
|
||||
*/
|
||||
caption: PropTypes.string.isRequired,
|
||||
/**
|
||||
* An array of column headers (see `Table` props).
|
||||
*/
|
||||
headers: PropTypes.arrayOf(
|
||||
PropTypes.shape( {
|
||||
hiddenByDefault: PropTypes.bool,
|
||||
defaultSort: PropTypes.bool,
|
||||
isSortable: PropTypes.bool,
|
||||
key: PropTypes.string,
|
||||
label: PropTypes.node,
|
||||
required: PropTypes.bool,
|
||||
} )
|
||||
),
|
||||
/**
|
||||
* An integer with the number of rows to display.
|
||||
*/
|
||||
numberOfRows: PropTypes.number,
|
||||
};
|
||||
|
||||
TablePlaceholder.defaultProps = {
|
||||
numberOfRows: 5,
|
||||
};
|
||||
|
||||
export default TablePlaceholder;
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createElement } from '@wordpress/element';
|
||||
import { range } from 'lodash';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Table from './table';
|
||||
import { QueryProps, TableHeader } from './types';
|
||||
|
||||
type TablePlaceholderProps = {
|
||||
/** An object of the query parameters passed to the page */
|
||||
query?: QueryProps;
|
||||
/** A label for the content in this table. */
|
||||
caption: string;
|
||||
/** An integer with the number of rows to display. */
|
||||
numberOfRows?: number;
|
||||
/**
|
||||
* Which column should be the row header, defaults to the first item (`0`) (but could be set to `1`, if the first col
|
||||
* is checkboxes, for example). Set to false to disable row headers.
|
||||
*/
|
||||
rowHeader?: number | false;
|
||||
/** An array of column headers (see `Table` props). */
|
||||
headers: Array< TableHeader >;
|
||||
};
|
||||
|
||||
/**
|
||||
* `TablePlaceholder` behaves like `Table` but displays placeholder boxes instead of data. This can be used while loading.
|
||||
*/
|
||||
const TablePlaceholder: React.VFC< TablePlaceholderProps > = ( {
|
||||
query,
|
||||
caption,
|
||||
headers,
|
||||
numberOfRows = 5,
|
||||
...props
|
||||
} ) => {
|
||||
const rows = range( numberOfRows ).map( () =>
|
||||
headers.map( () => ( {
|
||||
display: <span className="is-placeholder" />,
|
||||
} ) )
|
||||
);
|
||||
const tableProps = { query, caption, headers, numberOfRows, ...props };
|
||||
return (
|
||||
<Table
|
||||
ariaHidden={ true }
|
||||
className="is-loading"
|
||||
rows={ rows }
|
||||
{ ...tableProps }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default TablePlaceholder;
|
|
@ -2,6 +2,7 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { EmptyTable } from '@woocommerce/components';
|
||||
import { createElement } from '@wordpress/element';
|
||||
|
||||
export const Basic = () => <EmptyTable>There are no entries.</EmptyTable>;
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { TableCard } from '@woocommerce/components';
|
||||
import { useState } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { headers, rows, summary } from './index';
|
||||
|
||||
const TableCardExample = () => {
|
||||
const [ { query }, setState ] = useState( {
|
||||
query: {
|
||||
paged: 1,
|
||||
},
|
||||
} );
|
||||
return (
|
||||
<TableCard
|
||||
title="Revenue last week"
|
||||
rows={ rows }
|
||||
headers={ headers }
|
||||
onQueryChange={ ( param ) => ( value ) =>
|
||||
setState( {
|
||||
query: {
|
||||
[ param ]: value,
|
||||
},
|
||||
} ) }
|
||||
query={ query }
|
||||
rowsPerPage={ 7 }
|
||||
totalRows={ 10 }
|
||||
summary={ summary }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Basic = () => <TableCardExample />;
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Admin/components/TableCard',
|
||||
component: TableCard,
|
||||
};
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { TableCard } from '@woocommerce/components';
|
||||
import { useState, createElement } from '@wordpress/element';
|
||||
import { Button } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { headers, rows, summary } from './index';
|
||||
|
||||
const TableCardExample = () => {
|
||||
const [ { query }, setState ] = useState( {
|
||||
query: {
|
||||
paged: 1,
|
||||
},
|
||||
} );
|
||||
return (
|
||||
<TableCard
|
||||
title="Revenue last week"
|
||||
rows={ rows }
|
||||
headers={ headers }
|
||||
onQueryChange={ ( param ) => ( value ) =>
|
||||
setState( {
|
||||
// @ts-expect-error: ignore for storybook
|
||||
query: {
|
||||
[ param ]: value,
|
||||
},
|
||||
} ) }
|
||||
query={ query }
|
||||
rowsPerPage={ 7 }
|
||||
totalRows={ 10 }
|
||||
summary={ summary }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const TableCardWithActionsExample = () => {
|
||||
const [ { query }, setState ] = useState( {
|
||||
query: {
|
||||
paged: 1,
|
||||
},
|
||||
} );
|
||||
|
||||
const [ action1Text, setAction1Text ] = useState( 'Action 1' );
|
||||
const [ action2Text, setAction2Text ] = useState( 'Action 2' );
|
||||
|
||||
return (
|
||||
<TableCard
|
||||
actions={ [
|
||||
<Button
|
||||
key={ 0 }
|
||||
onClick={ () => {
|
||||
setAction1Text( 'Action 1 Clicked' );
|
||||
} }
|
||||
>
|
||||
{ action1Text }
|
||||
</Button>,
|
||||
<Button
|
||||
key={ 0 }
|
||||
onClick={ () => {
|
||||
setAction2Text( 'Action 2 Clicked' );
|
||||
} }
|
||||
>
|
||||
{ action2Text }
|
||||
</Button>,
|
||||
] }
|
||||
title="Revenue last week"
|
||||
rows={ rows }
|
||||
headers={ headers }
|
||||
onQueryChange={ ( param ) => ( value ) =>
|
||||
setState( {
|
||||
// @ts-expect-error: ignore for storybook
|
||||
query: {
|
||||
[ param ]: value,
|
||||
},
|
||||
} ) }
|
||||
query={ query }
|
||||
rowsPerPage={ 7 }
|
||||
totalRows={ 10 }
|
||||
summary={ summary }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Basic = () => <TableCardExample />;
|
||||
export const Actions = () => <TableCardWithActionsExample />;
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Admin/components/TableCard',
|
||||
component: TableCard,
|
||||
};
|
|
@ -3,17 +3,21 @@
|
|||
*/
|
||||
import { Card } from '@wordpress/components';
|
||||
import { TablePlaceholder } from '@woocommerce/components';
|
||||
import { createElement } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { headers } from './index';
|
||||
|
||||
export const Basic = () => (
|
||||
<Card size={ null }>
|
||||
<TablePlaceholder caption="Revenue last week" headers={ headers } />
|
||||
</Card>
|
||||
);
|
||||
export const Basic = () => {
|
||||
return (
|
||||
/* @ts-expect-error: size must be one of small, medium, largel, xSmall, extraSmall. */
|
||||
<Card size={ null }>
|
||||
<TablePlaceholder caption="Revenue last week" headers={ headers } />
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Admin/components/TablePlaceholder',
|
|
@ -3,14 +3,18 @@
|
|||
*/
|
||||
import { Card, CardFooter } from '@wordpress/components';
|
||||
import { TableSummaryPlaceholder } from '@woocommerce/components';
|
||||
import { createElement } from '@wordpress/element';
|
||||
|
||||
export const Basic = () => (
|
||||
<Card>
|
||||
<CardFooter justify="center">
|
||||
<TableSummaryPlaceholder />
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
export const Basic = () => {
|
||||
return (
|
||||
<Card>
|
||||
{ /* @ts-expect-error: justify is missing from the latest type def. */ }
|
||||
<CardFooter justify="center">
|
||||
<TableSummaryPlaceholder />
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Admin/components/TableSummaryPlaceholder',
|
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
import { Card } from '@wordpress/components';
|
||||
import { Table } from '@woocommerce/components';
|
||||
import { createElement } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -20,17 +21,20 @@ export const Basic = () => (
|
|||
</Card>
|
||||
);
|
||||
|
||||
export const NoDataCustomMessage = () => (
|
||||
<Card size={ null }>
|
||||
<Table
|
||||
caption="Revenue last week"
|
||||
rows={ [] }
|
||||
headers={ headers }
|
||||
rowKey={ ( row ) => row[ 0 ].value }
|
||||
emptyMessage="Custom empty message"
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
export const NoDataCustomMessage = () => {
|
||||
return (
|
||||
/* @ts-expect-error: size must be one of small, medium, largel, xSmall, extraSmall. */
|
||||
<Card size={ null }>
|
||||
<Table
|
||||
caption="Revenue last week"
|
||||
rows={ [] }
|
||||
headers={ headers }
|
||||
rowKey={ ( row ) => row[ 0 ].value }
|
||||
emptyMessage="Custom empty message"
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'WooCommerce Admin/components/Table',
|
|
@ -1,17 +1,17 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import PropTypes from 'prop-types';
|
||||
import { createElement } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* A component to display summarized table data - the list of data passed in on a single line.
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {Array} props.data
|
||||
* @return {Object} -
|
||||
* Internal dependencies
|
||||
*/
|
||||
const TableSummary = ( { data } ) => {
|
||||
import { TableSummaryProps } from './types';
|
||||
|
||||
/**
|
||||
* A component to display summarized table data - the list of data passed in on a single line.
|
||||
*/
|
||||
const TableSummary = ( { data }: TableSummaryProps ) => {
|
||||
return (
|
||||
<ul className="woocommerce-table__summary" role="complementary">
|
||||
{ data.map( ( { label, value }, i ) => (
|
||||
|
@ -28,13 +28,6 @@ const TableSummary = ( { data } ) => {
|
|||
);
|
||||
};
|
||||
|
||||
TableSummary.propTypes = {
|
||||
/**
|
||||
* An array of objects with `label` & `value` properties, which display on a single line.
|
||||
*/
|
||||
data: PropTypes.array,
|
||||
};
|
||||
|
||||
export default TableSummary;
|
||||
|
||||
/**
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue