merge changes

This commit is contained in:
Paul Sealock 2022-06-10 12:26:26 +12:00
commit 4fa634ee0e
160 changed files with 3080 additions and 1456 deletions

View File

@ -25,7 +25,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 successfully run tests with your changes locally?
- [ ] Have you created a changelog file for each project being changed, ie `pnpm nx changelog <project>`?
- [ ] Have you created a changelog file for each project being changed, ie `pnpm changelog add --filter=<project>`?
<!-- Mark completed items with an [x] -->

View File

@ -17,14 +17,9 @@ runs:
- name: Install dependencies
shell: bash
working-directory: ${{ inputs.working_directory }}
run: pnpm install
- name: Install Composer dependencies
shell: bash
working-directory: ${{ inputs.working_directory }}
run: pnpm nx composer-install-no-dev woocommerce
run: COMPOSER_NO_DEV=1 pnpm install
- name: Run build
shell: bash
working-directory: ${{ inputs.working_directory }}
run: pnpm nx build woocommerce
run: pnpm exec turbo run build --filter=woocommerce

View File

@ -63,7 +63,7 @@ jobs:
uses: ./.github/actions/install-build
- name: Build Admin feature config
run: pnpm nx build:feature-config woocommerce
run: pnpm build:feature-config --filter=woocommerce
- name: Add PHP8 Compatibility.
run: |
@ -75,7 +75,7 @@ jobs:
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/
pnpm nx composer-dump-autoload woocommerce
composer dump-autoload
fi
- name: Init DB and WP
@ -83,4 +83,4 @@ jobs:
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }}
- name: Run tests
run: pnpm nx test-unit woocommerce
run: pnpm exec turbo run test --filter=woocommerce

View File

@ -49,14 +49,16 @@ jobs:
- name: Build Admin feature config
run: |
pnpm nx build:feature-config woocommerce
pnpm build:feature-config --filter=woocommerce
- name: Init DB and WP
run: pnpm nx install-unit-test-db woocommerce
working-directory: plugins/woocommerce
run: bash tests/bin/install.sh woo_test root root 127.0.0.1 latest
- name: Run unit tests with code coverage. Allow to fail.
working-directory: plugins/woocommerce
run: |
pnpm nx test-code-coverage woocommerce
RUN_CODE_COVERAGE=1 bash tests/bin/phpunit.sh
exit 0
- name: Send code coverage to Codecov.

View File

@ -12,13 +12,15 @@ jobs:
- name: Install prerequisites
run: |
npm install -g pnpm@^6.24.2
npm -g i @wordpress/env
pnpm install
pnpm build:feature-config --filter=woocommerce
- name: Run analyzer
id: run
run: ./tools/code-analyzer/bin/dev analyzer "$GITHUB_HEAD_REF" -o github
- name: Print results
id: results
run: echo "::set-output name=results::${{ steps.run.outputs.templates }}${{ steps.run.outputs.wphooks }}"
run: echo "::set-output name=results::${{ steps.run.outputs.templates }}${{ steps.run.outputs.wphooks }}${{ steps.run.outputs.schema }}${{ steps.run.outputs.database }}"
comment:
name: Add comment to hightlight changes
needs: analyze
@ -29,7 +31,7 @@ jobs:
id: find-comment
with:
issue-number: ${{ github.event.number }}
comment-author: woocommercebot
body-includes: New hook, template, or database changes in this PR
- name: Add comment
if: ${{ needs.analyze.outputs.results && (steps.find-comment.outputs.comment-id == '') }}
uses: actions/github-script@v5
@ -40,7 +42,7 @@ jobs:
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '## New hook or template changes in this PR${{ needs.analyze.outputs.results }}'
body: '## New hook, template, or database changes in this PR${{ needs.analyze.outputs.results }}'
})
- name: Update comment
if: ${{ needs.analyze.outputs.results && steps.find-comment.outputs.comment-id }}
@ -52,15 +54,19 @@ jobs:
comment_id: ${{ steps.find-comment.outputs.comment-id }},
owner: context.repo.owner,
repo: context.repo.repo,
body: '## New hook or template changes in this PR${{ needs.analyze.outputs.results }}'
body: '## New hook, template, or database changes in this PR${{ needs.analyze.outputs.results }}'
})
- name: Delete comment
if: ${{ !needs.analyze.outputs.results && steps.find-comment.outputs.comment-id }}
uses: izhangzhihao/delete-comment@master
uses: actions/github-script@v5
with:
github_token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
delete_user_name: woocommercebot
issue_number: ${{ github.event.number }}
github-token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
script: |
github.rest.issues.deleteComment({
comment_id: ${{ steps.find-comment.outputs.comment-id }},
owner: context.repo.owner,
repo: context.repo.repo
})
- name: Add label
if: ${{ needs.analyze.outputs.results }}
uses: actions/github-script@v5

View File

@ -1,5 +1,8 @@
name: Run lint checks potentially affecting projects across the monorepo
on: pull_request
on:
pull_request:
branches:
- 'trunk'
concurrency:
group: changelogger-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true

View File

@ -35,13 +35,7 @@ jobs:
uses: ./.github/actions/install-build
- name: Lint
run: |
pnpm nx build woocommerce-admin
pnpm nx lint woocommerce-admin
pnpm nx lint:js-packages woocommerce-admin
run: pnpm exec turbo run lint --filter='@woocommerce/admin-library...' --filter='!@woocommerce/e2e*' --filter='!@woocommerce/api'
- name: Test
run: |
pnpm nx build woocommerce-admin
pnpm nx test woocommerce-admin
pnpm nx test:packages woocommerce-admin
run: pnpm exec turbo run test --filter='@woocommerce/admin-library...' --filter='!@woocommerce/e2e*' --filter='!@woocommerce/api'

View File

@ -60,7 +60,7 @@ jobs:
- name: Build Admin feature config
run: |
pnpm nx build:feature-config woocommerce
pnpm build:feature-config --filter=woocommerce
- name: Add PHP8 Compatibility.
run: |
@ -72,7 +72,7 @@ jobs:
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/
pnpm nx composer-dump-autoload woocommerce
composer dump-autoload
fi
- name: Init DB and WP
@ -80,4 +80,4 @@ jobs:
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }}
- name: Run tests
run: pnpm nx test-unit woocommerce
run: pnpm exec turbo run test --filter=woocommerce

View File

@ -20,14 +20,14 @@ $release_day_of_month = (int) date( 'j', $release_time );
// If 26 days from now isn't the second Tuesday, then it's not code freeze day.
if ( 'Tuesday' !== $release_day_of_week || $release_day_of_month < 8 || $release_day_of_month > 14 ) {
echo 'Info: Today is not the Thursday of the code freeze.' . PHP_EOL;
return;
exit( 1 );
}
$latest_version_with_release = get_latest_version_with_release();
if ( empty( $latest_version_with_release ) ) {
echo '*** Error: Unable to get latest version with release' . PHP_EOL;
return;
exit( 1 );
}
// Because we go from 5.9 to 6.0, we can get the next major_minor by adding 0.1 and formatting appropriately.
@ -60,6 +60,8 @@ if ( create_github_branch_from_branch( 'trunk', $release_branch_to_create ) ) {
} else if ( '422' === $github_api_response_code ) {
// The release branch already existed when GitHub returns a 422 status.
echo "Notice: Unable to create {$release_branch_to_create} branch. Maybe it already exists? Skipping..." . PHP_EOL;
exit( 1 );
} else {
echo "*** Error: Unable to create {$release_branch_to_create}" . PHP_EOL;
exit( 1 );
}

View File

@ -177,7 +177,7 @@ jobs:
- name: Load docker images and start containers.
working-directory: package/woocommerce
run: pnpm nx docker-up woocommerce
run: pnpm docker:up --filter=woocommerce
- name: Run tests command.
working-directory: package/woocommerce/plugins/woocommerce

View File

@ -109,7 +109,7 @@ jobs:
working-directory: package/woocommerce
env:
LATEST_WP_VERSION_MINUS: ${{ matrix.wp }}
run: pnpm nx docker-up woocommerce
run: pnpm docker:up --filter=woocommerce
- name: Run tests command.
working-directory: package/woocommerce/plugins/woocommerce
@ -188,7 +188,7 @@ jobs:
working-directory: package/woocommerce
env:
LATEST_WP_VERSION_MINUS: ${{ matrix.wp }}
run: pnpm nx docker-up woocommerce
run: pnpm docker:up --filter=woocommerce
- name: Run tests command.
working-directory: package/woocommerce/plugins/woocommerce

3
.gitignore vendored
View File

@ -92,3 +92,6 @@ allure-results
/plugins/woocommerce/e2e/output
/plugins/woocommerce/e2e/report
/plugins/woocommerce/e2e/storage
# Turborepo
.turbo

View File

@ -2,4 +2,3 @@
. "$(dirname "$0")/_/husky.sh"
pnpm install
pnpm nx affected --target="composer-install" --base=ORIG_HEAD --head=HEAD

View File

@ -34,14 +34,7 @@ The port # might be different depending on your `.wp-env.override.json` configur
Once you have WP-ENV container up, we need to run a few commands to start developing.
1. Run `pnpm install` to install npm modules.
2. Run `pnpm nx build woocommerce` to build core.
3. Run `pnpm nx composer-install woocommerce` to install PHP dependencies.
If you don't have Composer available locally, run the following command. It runs the command in WP-ENV container.
`wp-env run composer composer install`
You might also want to run `pnpm start` to watch your CSS and JS changes if you are working on the frontend.
2. Run `pnpm exec turbo run build --filter=woocommerce` to build core.
You're now ready to develop!

View File

@ -1,24 +1,55 @@
<p align="center"><a href="https://woocommerce.com/"><img src="https://woocommerce.com/wp-content/themes/woo/images/logo-woocommerce@2x.png" alt="WooCommerce"></a></p>
<p align="center">
<a href="https://packagist.org/packages/woocommerce/woocommerce"><img src="https://poser.pugx.org/woocommerce/woocommerce/license" alt="license"></a>
<a href="https://packagist.org/packages/woocommerce/woocommerce"><img src="https://poser.pugx.org/woocommerce/woocommerce/v/stable" alt="Latest Stable Version"></a>
<img src="https://img.shields.io/wordpress/plugin/dt/woocommerce.svg" alt="WordPress.org downloads">
<img src="https://img.shields.io/wordpress/plugin/r/woocommerce.svg" alt="WordPress.org rating">
<a href="https://github.com/woocommerce/woocommerce/actions/workflows/ci.yml"><img src="https://github.com/woocommerce/woocommerce/actions/workflows/ci.yml/badge.svg?branch=trunk" alt="Build Status"></a>
<a href="https://codecov.io/gh/woocommerce/woocommerce"><img src="https://codecov.io/gh/woocommerce/woocommerce/branch/trunk/graph/badge.svg" alt="codecov"></a>
</p>
Welcome to the WooCommerce Monorepo on GitHub. Here you can find all of the packages, plugins, and tools used in the development of the core WooCommerce plugin as well as WooCommerce extensions. You can browse the source, look at open issues, contribute code, and keep tracking of ongoing development.
Welcome to the WooCommerce repository on GitHub. Here you can browse the source, look at open issues and keep track of development. We recommend all developers to follow the [WooCommerce development blog](https://woocommerce.wordpress.com/) to stay up to date about everything happening in the project. You can also [follow @DevelopWC](https://twitter.com/DevelopWC) on Twitter for the latest development updates.
We recommend all developers to follow the [WooCommerce development blog](https://woocommerce.wordpress.com/) to stay up to date about everything happening in the project. You can also [follow @DevelopWC](https://twitter.com/DevelopWC) on Twitter for the latest development updates.
If you are not a developer, please use the [WooCommerce plugin page](https://wordpress.org/plugins/woocommerce/) on WordPress.org.
## Repository Structure
## Documentation
* [WooCommerce Documentation](https://docs.woocommerce.com/)
* [WooCommerce Developer Documentation](https://github.com/woocommerce/woocommerce/wiki)
* [WooCommerce Code Reference](https://docs.woocommerce.com/wc-apidocs/)
* [WooCommerce REST API Docs](https://woocommerce.github.io/woocommerce-rest-api-docs/)
* [Setting up a development environment](https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment)
* [**Plugins**](plugins): Our repository contains plugins that relate to or otherwise aid in the development of WooCommerce.
* [**WooCommerce Core**](plugins/woocommerce): The core WooCommerce plugin is available in the plugins directory.
* [**Packages**](packages): Contained within the packages directory are all of the [PHP](packages/php) and [JavaScript](packages/js) provided for the community. Some of these are internal dependencies and are marked with an `internal-` prefix.
* [**Tools**](tools): We also have a growing number of tools within our repository. Many of these are intended to be utilities and scripts for use in the monorepo, but, this directory may also contain external tools.
## Getting Started
To get up and running within the WooCommerce Monorepo, you will need to make sure that you have installed all of the prerequisites.
### Prerequisites
* [NVM](https://github.com/nvm-sh/nvm#installing-and-updating): While you can always install Node through other means, we recommend using NVM to ensure you're aligned with the version used by our development teams. Our repository contains [an `.nvmrc` file](.nvmrc) which helps ensure you are using the correct version of Node.
* [PNPM](https://pnpm.io/installation): Our repository utilizes PNPM to manage project dependencies and run various scripts involved in building and testing projects.
* [PHP 7.2+](https://www.php.net/manual/en/install.php): WooCommerce Core currently features a minimum PHP version of 7.2. While you don't need to use it to run a local development environment, you will need it to utilize Composer.
* [Composer](https://getcomposer.org/doc/00-intro.md): We use Composer to manage all of the dependencies for PHP packages and plugins.
Once you've installed all of the prerequisites, you can run the following commands.
```bash
# Ensure that you're using the correct version of Node
nvm use
# Install all of the NPM and Composer dependencies within the Monorepo
pnpm install
```
### Building, Linting, and Testing
Our repository uses [Turborepo](https://turborepo.org) for running `build`, `lint`, `test`, and `e2e` commands. This tool ensures that all dependencies of a package, plugin, or tool are prepared before running a command. It also provides caching for command outputs in order to ensure that work is not performed unnecessarily.
Without any additional flags, running a command will execute it against every project in the monorepo. For example, `pnpm exec turbo run build` will build all of the projects within the monorepo. `pnpm exec turbo run test` will run unit tests for all of the projects within the monorepo.
This behavior can be desireable, as the cache should ensure anything that has not changed is not rebuilt. There are times, however, that you may want to explicitly run a command against a specific project.
This can be done using the `--filter` flag. For example, running `pnpm exec turbo run build --filter=woocommerce` will build the WooCommerce plugin, as well as all of the dependencies required for the plugin to function.
The `--filter` syntax also supports paths, such as `--filter='./plugins/**'` to build all of the plugins in the monorepo. [You can read more about the filtering syntax in Turborepo's documentation](https://turborepo.org/docs/core-concepts/filtering).
### Project-Specific Commands
Outside of the above `turbo` commands, there may be times where you want to run a command on a specific project. This can _also_ be done using the `--filter` syntax, however, you will run these commands using `pnpm`. For example, `pnpm postinstall --filter=woocommerce` will run the `"postinstall"` script from `plugins/woocommerce/package.json`.
## Development Environments
Our repository makes use of [the `@wordpress/env` package](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/) for providing development environments out-of-the-box. Once you have installed the package and its related dependencies, you should be able to run `wp-env start` in any of the `plugins/` folders. This will start a development environment and provide you with a URL to begin testing code using.
## Reporting Security Issues
To disclose a security issue to our team, [please submit a report via HackerOne here](https://hackerone.com/automattic/).
@ -33,12 +64,3 @@ This repository is not suitable for support. Please don't use our issue tracker
* For customizations, you may want to check our list of [WooExperts](https://woocommerce.com/experts/) or [Codeable](https://codeable.io/).
Support requests in issues on this repository will be closed on sight.
## Contributing to WooCommerce
If you have a patch or have stumbled upon an issue with WooCommerce core, you can contribute this back to the code. Please read our [contributor guidelines](https://github.com/woocommerce/woocommerce/blob/trunk/.github/CONTRIBUTING.md) for more information how you can do this.
<p align="center">
<br/><br/>
Made with 💜 by <a href="https://woocommerce.com/">WooCommerce</a>.<br/>
<a href="https://woocommerce.com/careers/">We're hiring</a>! Come work with us!
</p>

34
nx.json
View File

@ -1,34 +0,0 @@
{
"extends": "@nrwl/workspace/presets/npm.json",
"npmScope": "woocommerce-monorepo",
"tasksRunnerOptions": {
"default": {
"runner": "@nrwl/workspace/tasks-runners/default",
"options": {
"cacheableOperations": [
"build",
"test",
"lint",
"package"
]
}
}
},
"targetDependencies": {
"build": [
{
"target": "build",
"projects": "dependencies"
}
],
"package": [
{
"target": "package",
"projects": "dependencies"
}
]
},
"affected": {
"defaultBase": "trunk"
}
}

View File

@ -23,15 +23,8 @@
"create-extension": "node ./tools/create-extension/index.js"
},
"devDependencies": {
"@automattic/nx-composer": "^0.1.0",
"@babel/preset-env": "^7.16.11",
"@babel/runtime": "^7.17.2",
"@nrwl/cli": "^13.3.4",
"@nrwl/devkit": "^13.1.4",
"@nrwl/linter": "^13.3.4",
"@nrwl/tao": "13.3.4",
"@nrwl/web": "^13.3.4",
"@nrwl/workspace": "^13.3.4",
"@storybook/addon-a11y": "^6.4.19",
"@storybook/addon-actions": "^6.4.19",
"@storybook/addon-console": "^1.2.3",
@ -72,6 +65,7 @@
"request": "^2.88.2",
"sass": "^1.49.9",
"sass-loader": "^10.2.1",
"turbo": "^1.2.9",
"typescript": "4.2.4",
"url-loader": "^1.1.2",
"webpack": "^5.70.0"

View File

@ -51,6 +51,7 @@
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"build": "tsc --build",
"start": "tsc --build --watch",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",

View File

@ -1,31 +0,0 @@
{
"root": "packages/js/admin-e2e-tests",
"sourceRoot": "packages/js/admin-e2e-tests/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
},
"clean": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "clean"
}
}
}
}

View File

@ -22,7 +22,7 @@ For local setup, create a `.env` file in this folder with the three required val
Alternatively, these values can be passed in via the command line. For example:
```shell
BASE_URL=http://localhost:8084 USER_KEY=admin USER_SECRET=password npm run test:api
BASE_URL=http://localhost:8084 USER_KEY=admin USER_SECRET=password npm run e2e:api
```
When using a username and password combination instead of a consumer secret and consumer key, make sure to have the [JSON Basic Authentication plugin](https://github.com/WP-API/Basic-Auth) installed and activated on the test site.
@ -43,7 +43,7 @@ The following optional variables can be set in your local `.env` file:
To verify that everything is configured correctly, the following test script is available:
```shell
npm run test:hello
npm run e2e:hello
```
This tests connectivity to the API by validating connection to the following:
@ -56,7 +56,7 @@ This tests connectivity to the API by validating connection to the following:
To run all of the API tests, you can use the following command:
```shell
npm run test:api
npm run e2e:api
```
### Running groups of tests
@ -66,7 +66,7 @@ To run a specific group of tests, you can use the `npm test -- --group=` command
For example, if you wanted to only run the orders API tests, you can use the following:
```shell
npm test -- --group=orders
npm e2e -- --group=orders
```
Alternatively, you can use `jest` to run test groups:

View File

@ -4,9 +4,9 @@
"description": "API tests for WooCommerce",
"main": "index.js",
"scripts": {
"test": "jest",
"test:api": "jest --group=api",
"test:hello": "jest --group=hello",
"e2e": "jest",
"e2e:api": "jest --group=api",
"e2e:hello": "jest --group=hello",
"make:collection": "node utils/api-collection/build-collection.js",
"report": "allure generate --clean && allure serve",
"lint": "eslint data endpoints tests utils --ext=js,ts,tsx",

View File

@ -1,38 +0,0 @@
{
"root": "packages/js/api-core-tests/",
"sourceRoot": "packages/js/api-core-tests",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/api-core-tests"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
},
"test-hello": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test:hello"
}
},
"make-collection": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "make:collection"
}
},
"test-api": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test:api"
}
}
}
}

View File

@ -27,6 +27,7 @@
"sideEffects": false,
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"clean": "rm -rf ./dist ./tsconfig.tsbuildinfo",
"compile": "tsc -b",
"build": "pnpm run clean && npm run compile",

View File

@ -1,44 +0,0 @@
{
"root": "packages/js/api/",
"sourceRoot": "packages/js/api/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/api"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"clean": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "clean"
}
},
"compile": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "compile"
}
},
"lint": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "lint"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
}
}
}

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add tour kit component

View File

@ -26,7 +26,9 @@
],
"types": "build-types",
"dependencies": {
"@automattic/calypso-color-schemes": "^2.1.1",
"@automattic/interpolate-components": "^1.2.0",
"@automattic/tour-kit": "^1.0.0",
"@woocommerce/csv-export": "workspace:*",
"@woocommerce/currency": "workspace:*",
"@woocommerce/data": "workspace:*",
@ -93,6 +95,7 @@
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^13.5.0",
"@types/wordpress__viewport": "^2.5.4",
"@woocommerce/eslint-plugin": "workspace:*",
"@woocommerce/internal-style-build": "workspace:*",
"@wordpress/browserslist-config": "^4.1.1",
@ -112,6 +115,7 @@
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"build": "pnpm run build:js && pnpm run build:css",
"build:js": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
"build:css": "webpack",
@ -120,9 +124,8 @@
"lint:fix": "eslint src --ext=js,ts,tsx --fix",
"prepack": "pnpm run clean && pnpm run build",
"start": "concurrently \"tsc --build ./tsconfig.json --watch\" \"webpack --watch\"",
"test": "pnpm run build && pnpm run test:nobuild",
"test:nobuild": "jest --config ./jest.config.json",
"test:update-snapshots": "pnpm run test:nobuild -- --updateSnapshot",
"test": "jest --config ./jest.config.json",
"test:update-snapshots": "pnpm run test -- --updateSnapshot",
"test-staged": "jest --bail --config ./jest.config.json --findRelatedTests"
},
"lint-staged": {

View File

@ -1,32 +0,0 @@
{
"root": "packages/js/components",
"sourceRoot": "packages/js/components/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/components"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
}
}
}

View File

@ -58,3 +58,5 @@ export { default as ViewMoreList } from './view-more-list';
export { default as WebPreview } from './web-preview';
export { Badge } from './badge';
export { DynamicForm } from './dynamic-form';
export { default as TourKit } from './tour-kit';
export * as TourKitTypes from './tour-kit/types';

View File

@ -1,3 +1,8 @@
/**
* External Dependencies
*/
@import '@automattic/tour-kit/dist/esm/styles.scss';
/**
* Internal Dependencies
*/
@ -38,3 +43,4 @@
@import 'web-preview/style.scss';
@import 'badge/style.scss';
@import 'dynamic-form/style.scss';
@import 'tour-kit/style.scss';

View File

@ -0,0 +1,116 @@
# tour-kit
A Woocommerce Tour Kit variant based on [@automattic/tour-kit](https://github.com/Automattic/wp-calypso/blob/trunk/packages/tour-kit/README.md) for configurable guided tours. Contains some optional effects (like spotlight and overlay) that can be enabled/disabled depending on the desired use..
Uses [Popper.js](https://popper.js.org/) underneath (also customizable via tour configuration).
## Usage
A typical expected workflow builds around:
1. Define the criteria for showing a tour.
2. Define a configuration for the tour, passing along a handler for closing.
3. Render it (or not).
### Sample
```jsx
import { TourKit } from '@woocommerce/components';
function Tour() {
// 1. Define the criteria for showing a tour:
const [ showTour, setShowTour ] = useState( true );
// 2. Define a configuration for the tour, passing along a handler for closing.
const config = {
steps: [
{
referenceElements: {
desktop: '.render-step-near-me',
},
meta: {
heading: 'Lorem ipsum dolor sit amet.',
descriptions: {
desktop: 'Lorem ipsum dolor sit amet.',
},
primaryButtonText: "Done"
},
},
],
closeHandler: () => setShowTour( false ),
options: {},
};
// 3. Render it (or not):
if ( ! showTour ) {
return null;
}
return <TourKit config={ config } />;
}
```
## Accessibility
### Keyboard Navigation
When a tour is rendered and focused, the following functionality exists:
- Close the tour on `ESC` key (in minimized view)
- Go to previous/next step on `left/right` arrow keys (in step view)
## Configuration
The main API for configuring a tour is the config object. See example usage and [types.ts](./types.ts) for the full definition.
`config.steps`: An array of objects that define the content we wish to render on the page. Each step defined by:
- `referenceElements` (optional): A set of `desktop` & `mobile` selectors to render the step near.
- `focusElement` (optional): A set of `desktop` & `mobile` & `iframe` selectors to automatically focus.
- `meta`: Arbitrary object that encloses the content we want to render for each step.
- `classNames` (optional): An array or CSV of CSS classes applied to a step.
`config.closeHandler`: The callback responsible for closing the tour.
- `tourStep`: A React component that will be called to render each step. Receives the following properties:
- `steps`: The steps defined for the tour.
- `currentStepIndex`
- `onDismiss`: Handler that dismissed/closes the tour.
- `onNext`: Handler that progresses the tour to the next step.
- `onPrevious`: Handler that takes the tour to the previous step.
- `onMinimize`: Handler that minimizes the tour (passes rendering to `tourMinimized`).
- `setInitialFocusedElement`: A dispatcher that assigns an element to be initially focused when a step renders (see examples).
- `onGoToStep`: Handler that progresses the tour to a given step index.
- `tourMinimized`: A React component that will be called to render a minimized view for the tour. Receives the following properties:
- `steps`: The steps defined for the tour.
- `currentStepIndex`
- `onDismiss`: Handler that dismissed/closes the tour.
- `onMaximize`: Handler that expands the tour (passes rendering to `tourStep`).
`config.options` (optional):
- `classNames` (optional): An array or CSV of CSS classes to enclose the main tour frame with.
- `effects`: An object to enable/disable/combine various tour effects:
- `spotlight`: Adds a semi-transparent overlay and highlights the reference element when provided with a transparent box over it. Expects an object with optional styles to override the default highlight/spotlight behavior when provided (default: spotlight wraps the entire reference element).
- `interactivity`: An object that configures whether the user is allowed to interact with the referenced element during the tour
- `styles`: CSS properties that configures the styles applied to the spotlight overlay
- `arrowIndicator`: Adds an arrow tip pointing at the reference element when provided.
- `overlay`: Includes the semi-transparent overlay for all the steps (also blocks interactions with the rest of the page)
- `autoScroll`: The page scrolls up and down automatically such that the step target element is visible to the user.
- `callbacks`: An object of callbacks to handle side effects from various interactions (see [types.ts](./src/types.ts)).
- `popperModifiers`: The tour uses Popper to position steps near reference elements (and for other effects). An implementation can pass its own modifiers to tailor the functionality further e.g. more offset or padding from the reference element.
- `tourRating` (optional - only in WPCOM Tour Kit variant):
- `enabled`: Whether to show rating in last step.
- `useTourRating`: (optional) A hook to provide the rating from an external source/state (see [types.ts](./src/types.ts)).
- `onTourRate`: (optional) A callback to fire off when a rating is submitted.
- `portalElementId`: A string that lets you customize under which DOM element the Tour will be appended.
`placement` (Optional) : Describes the preferred placement of the popper. Possible values are left-start, left, left-end, top-start, top, top-end, right-start, right, right-end, bottom-start, bottom, and bottom-end.

View File

@ -0,0 +1,29 @@
/**
* External dependencies
*/
import { Button, Flex, Icon } from '@wordpress/components';
import { closeSmall } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
import { createElement } from '@wordpress/element';
import { TourStepRendererProps } from '@automattic/tour-kit';
interface Props {
onDismiss: TourStepRendererProps[ 'onDismiss' ];
}
const StepControls: React.FunctionComponent< Props > = ( { onDismiss } ) => {
return (
<Flex className="woocommerce-tour-kit-step-controls" justify="flex-end">
<Button
className="woocommerce-tour-kit-step-controls__close-btn"
label={ __( 'Close Tour', 'woocommerce' ) }
icon={ <Icon icon={ closeSmall } viewBox="6 4 12 14" /> }
iconSize={ 16 }
size={ 16 }
onClick={ onDismiss( 'close-btn' ) }
></Button>
</Flex>
);
};
export default StepControls;

View File

@ -0,0 +1,96 @@
/**
* External dependencies
*/
import { Button } from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { createElement } from '@wordpress/element';
/**
* Internal dependencies
*/
import type { WooTourStepRendererProps } from '../types';
type Props = Omit<
WooTourStepRendererProps,
'onMinimize' | 'setInitialFocusedElement'
>;
const StepNavigation: React.FunctionComponent< Props > = ( {
currentStepIndex,
onNextStep,
onPreviousStep,
onDismiss,
steps,
} ) => {
const isFirstStep = currentStepIndex === 0;
const isLastStep = currentStepIndex === steps.length - 1;
const { primaryButton = { text: '', isDisabled: false } } = steps[
currentStepIndex
].meta;
const NextButton = (
<Button
className="woocommerce-tour-kit-step-navigation__next-btn"
isPrimary
disabled={ primaryButton.isDisabled }
onClick={ onNextStep }
>
{ primaryButton.text || __( 'Next', 'woocommerce' ) }
</Button>
);
const BackButton = (
<Button
className="woocommerce-tour-kit-step-navigation__back-btn"
isSecondary
onClick={ onPreviousStep }
>
{ __( 'Back', 'woocommerce' ) }
</Button>
);
const renderButtons = () => {
if ( isFirstStep ) {
return <div>{ NextButton }</div>;
}
if ( isLastStep ) {
return (
<div>
{ BackButton }
<Button
isPrimary
disabled={ primaryButton.isDisabled }
className="woocommerce-tour-kit-step-navigation__done-btn"
onClick={ onDismiss( 'done-btn' ) }
>
{ primaryButton.text || __( 'Done', 'woocommerce' ) }
</Button>
</div>
);
}
return (
<div>
{ BackButton }
{ NextButton }
</div>
);
};
return (
<div className="woocommerce-tour-kit-step-navigation">
<div className="woocommerce-tour-kit-step-navigation__step">
{ sprintf(
/* translators: current progress in tour, eg: "Step 2 of 4" */
__( 'Step %1$d of %2$d', 'woocommerce' ),
currentStepIndex + 1,
steps.length
) }
</div>
{ renderButtons() }
</div>
);
};
export default StepNavigation;

View File

@ -0,0 +1,115 @@
/**
* External dependencies
*/
import { withViewportMatch } from '@wordpress/viewport';
import { Card, CardBody, CardFooter, CardHeader } from '@wordpress/components';
import { createElement, useEffect } from '@wordpress/element';
/**
* Internal dependencies
*/
import StepNavigation from './step-navigation';
import StepControls from './step-controls';
import type { WooTourStepRendererProps } from '../types';
const getFocusElement = (
focusElementSelector: string | null,
iframeSelector: string | null
) => {
if ( ! focusElementSelector ) {
return null;
}
if ( iframeSelector ) {
const iframeElement = document.querySelector< HTMLIFrameElement >(
iframeSelector
);
if ( ! iframeElement ) {
return null;
}
const innerDoc =
iframeElement.contentDocument ||
( iframeElement.contentWindow &&
iframeElement.contentWindow.document );
if ( ! innerDoc ) {
return null;
}
return innerDoc.querySelector< HTMLElement >( focusElementSelector );
}
return document.querySelector< HTMLElement >( focusElementSelector );
};
const Step: React.FunctionComponent<
WooTourStepRendererProps & {
isViewportMobile: boolean;
}
> = ( {
steps,
currentStepIndex,
onDismiss,
onNextStep,
onPreviousStep,
setInitialFocusedElement,
onGoToStep,
isViewportMobile,
} ) => {
const { descriptions, heading } = steps[ currentStepIndex ].meta;
const description =
descriptions[ isViewportMobile ? 'mobile' : 'desktop' ] ??
descriptions.desktop;
const focusElementSelector =
steps[ currentStepIndex ].focusElement?.[
isViewportMobile ? 'mobile' : 'desktop'
] || null;
const iframeSelector =
steps[ currentStepIndex ].focusElement?.iframe || null;
const focusElement = getFocusElement(
focusElementSelector,
iframeSelector
);
/*
* Focus the element when step renders.
*/
useEffect( () => {
if ( focusElement ) {
setInitialFocusedElement( focusElement );
}
}, [ focusElement, setInitialFocusedElement ] );
return (
<Card className="woocommerce-tour-kit-step" isElevated>
<CardHeader isBorderless={ true } size="small">
<StepControls onDismiss={ onDismiss } />
</CardHeader>
<CardBody className="woocommerce-tour-kit-step__body" size="small">
<h2 className="woocommerce-tour-kit-step__heading">
{ heading }
</h2>
<p className="woocommerce-tour-kit-step__description">
{ description }
</p>
</CardBody>
<CardFooter isBorderless={ true } size="small">
<StepNavigation
currentStepIndex={ currentStepIndex }
onGoToStep={ onGoToStep }
onNextStep={ onNextStep }
onPreviousStep={ onPreviousStep }
onDismiss={ onDismiss }
steps={ steps }
></StepNavigation>
</CardFooter>
</Card>
);
};
export default withViewportMatch( {
isViewportMobile: '< medium',
} )( Step );

View File

@ -0,0 +1,42 @@
/**
* External dependencies
*/
import { createElement } from '@wordpress/element';
import TourKit, { TourStepRenderer } from '@automattic/tour-kit';
/**
* Internal dependencies
*/
import TourKitStep from './components/step';
import type { WooConfig } from './types';
interface Props {
config: WooConfig;
}
const defaultOptions = {
effects: { spotlight: {}, arrowIndicator: true },
};
const WooTourKit: React.FunctionComponent< Props > = ( { config } ) => {
return (
<TourKit
__temp__className={ 'woocommerce-tour-kit' }
config={ {
options: {
...defaultOptions,
...config.options,
},
...config,
renderers: {
tourStep: TourKitStep as TourStepRenderer,
// Disable minimize feature for woo tour kit.
tourMinimized: () => null,
},
} }
/>
);
};
export default WooTourKit;

View File

@ -0,0 +1,212 @@
/**
* External dependencies
*/
import { createElement, useState } from '@wordpress/element';
/**
* Internal dependencies
*/
import './style.scss';
import '../style.scss';
import WooTourKit from '..';
import type { WooConfig, WooOptions } from '../types';
export default {
title: 'WooCommerce Admin/components/TourKit',
component: WooTourKit,
};
const References = () => {
return (
<div className={ 'storybook__tourkit-references' }>
<div className={ 'storybook__tourkit-references-container' }>
<div className={ 'storybook__tourkit-references-a' }>
<p>Reference A</p>
</div>
<div className={ 'storybook__tourkit-references-b' }>
<p>Reference B</p>
<div style={ { display: 'grid', placeItems: 'center' } }>
<input
style={ { margin: 'auto', display: 'block' } }
></input>
</div>
</div>
<div className={ 'storybook__tourkit-references-c' }>
<p>Reference C</p>
</div>
<div className={ 'storybook__tourkit-references-d' }>
<p>Reference D</p>
</div>
</div>
</div>
);
};
const Tour = ( {
onClose,
options,
placement,
}: {
onClose: () => void;
options?: WooOptions;
placement?: WooConfig[ 'placement' ];
} ) => {
const config: WooConfig = {
placement,
steps: [
{
referenceElements: {
desktop: '.storybook__tourkit-references-a',
mobile: '.storybook__tourkit-references-a',
},
meta: {
heading: 'Change content',
descriptions: {
desktop:
'You can change the content and add any relevant links.',
mobile:
'You can change the content and add any relevant links.',
},
},
},
{
referenceElements: {
desktop: '.storybook__tourkit-references-b',
mobile: '.storybook__tourkit-references-b',
},
focusElement: {
desktop: '.storybook__tourkit-references-b input',
},
meta: {
heading: 'Shipping zones',
descriptions: {
desktop:
'We added a few shipping zones for you based on your location, but you can manage them at any time.',
mobile:
'A shipping zone is a geographic area where a certain set of shipping methods are offered.',
},
},
},
{
referenceElements: {
desktop: '.storybook__tourkit-references-c',
mobile: '.storybook__tourkit-references-c',
},
meta: {
heading: 'Shipping methods',
descriptions: {
desktop:
'We defaulted to some recommended shipping methods based on your store location, but you can manage them at any time within each shipping zone settings. ',
mobile:
'We defaulted to some recommended shipping methods based on your store location, but you can manage them at any time within each shipping zone settings. ',
},
},
},
{
referenceElements: {
desktop: '.storybook__tourkit-references-d',
mobile: '.storybook__tourkit-references-d',
},
meta: {
heading: 'Laura 4',
descriptions: {
desktop: 'Lorem ipsum dolor sit amet.',
mobile: 'Lorem ipsum dolor sit amet.',
},
primaryButton: {
isDisabled: true,
text: 'Keep editing',
},
},
},
],
closeHandler: onClose,
options: {
classNames: [ 'mytour' ],
...options,
},
};
return <WooTourKit config={ config } />;
};
const StoryTour = ( {
options = {},
placement,
}: {
options?: WooConfig[ 'options' ];
placement?: WooConfig[ 'placement' ];
} ) => {
const [ showTour, setShowTour ] = useState( false );
return (
<div className="storybook__tourkit">
<References />
{ ! showTour && (
<button onClick={ () => setShowTour( true ) }>
Start Tour
</button>
) }
{ showTour && (
<Tour
placement={ placement }
onClose={ () => setShowTour( false ) }
options={ options }
/>
) }
</div>
);
};
export const NoEffects = () => (
<StoryTour
options={ {
effects: {},
} }
/>
);
export const Spotlight = () => (
<StoryTour
options={ {
effects: { arrowIndicator: true, spotlight: {} },
} }
/>
);
export const Overlay = () => (
<StoryTour
options={ {
effects: { arrowIndicator: true, overlay: true },
} }
/>
);
export const SpotlightInteractivity = () => (
<StoryTour
options={ {
effects: {
spotlight: {
interactivity: {
rootElementSelector: '#root',
enabled: true,
},
},
},
} }
/>
);
export const AutoScroll = () => (
<>
<div style={ { height: '10vh' } }></div>
<StoryTour
options={ {
effects: {
autoScroll: {
behavior: 'smooth',
},
},
} }
/>
</>
);
export const Placement = () => <StoryTour placement={ 'left' } />;

View File

@ -0,0 +1,73 @@
.storybook__tourkit {
height: 500px;
}
.storybook__tourkit-step,
.storybook__tourkit-minimized {
padding: 10px;
}
.storybook__tourkit-step {
min-height: 150px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.storybook__tourkit-step-controls,
.storybook__tourkit-minimized-controls {
display: flex;
flex-direction: row-reverse;
}
.storybook__tourkit-references {
height: 400px;
margin-bottom: 30px;
overflow: auto;
resize: auto;
.storybook__tourkit-references-container {
height: 100%;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr 1fr;
gap: 0 0;
grid-auto-flow: row;
grid-template-areas:
'Ref-A Ref-B Ref-B Ref-B Ref-D'
'Ref-A Ref-B Ref-B Ref-B Ref-D'
'Ref-A Ref-C Ref-C Ref-C Ref-D'
'Ref-A Ref-C Ref-C Ref-C Ref-D';
}
.storybook__tourkit-references-a,
.storybook__tourkit-references-b,
.storybook__tourkit-references-c,
.storybook__tourkit-references-d {
display: grid;
> p {
align-self: center;
justify-self: center;
}
}
.storybook__tourkit-references-a {
background-color: silver;
grid-area: Ref-A;
}
.storybook__tourkit-references-b {
background-color: orange;
grid-area: Ref-B;
}
.storybook__tourkit-references-c {
background-color: palevioletred;
grid-area: Ref-C;
}
.storybook__tourkit-references-d {
background-color: gainsboro;
grid-area: Ref-D;
}
}

View File

@ -0,0 +1,66 @@
.woocommerce-tour-kit{
box-shadow: 0 0 8px rgba( 0, 0, 0, 0.15 );
position: absolute;
z-index: 9999;
.woocommerce-tour-kit-step {
width: 350px;
padding-bottom: 4px;
}
.woocommerce-tour-kit-step__body {
padding-top: 0;
padding-bottom: 8px;
}
.woocommerce-tour-kit-step__heading {
color: $studio-gray-100;
font-weight: 500;
font-size: 16px;
margin: 0;
letter-spacing: -0.1px;
line-height: 24px;
}
.woocommerce-tour-kit-step__description {
color: $studio-gray-80;
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 20px;
letter-spacing: -0.24px;
margin: 4px 0 0;
}
.woocommerce-tour-kit-step-controls__close-btn {
height: 16px;
min-width: 16px;
padding: 0;
svg {
fill: #1e1e1e;
}
}
.woocommerce-tour-kit-step-navigation {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
button {
margin-left: $gap;
}
}
.woocommerce-tour-kit-step-navigation__step {
color: $studio-gray-60;
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 20px;
}
.tour-kit-frame__arrow, .tour-kit-frame__arrow::before {
z-index: 1;
}
}

View File

@ -0,0 +1,61 @@
/**
* External dependencies
*/
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { createElement } from '@wordpress/element';
/**
* Internal dependencies
*/
import TourKit from '..';
jest.mock( '@automattic/calypso-config' );
const config = {
steps: [
{
referenceElements: {
desktop: '.render-step-near-me',
},
meta: {
heading: 'Step1',
descriptions: {
desktop: 'Description',
},
},
},
{
referenceElements: {
desktop: '.render-step-near-me',
},
meta: {
heading: 'Step2',
descriptions: {
desktop: 'Description',
},
},
},
],
closeHandler: () => jest.fn(),
options: {},
};
describe( 'TourKit', () => {
it( 'should render TourKit', () => {
const { queryByText } = render( <TourKit config={ config } /> );
expect( queryByText( 'Step1' ) ).toBeInTheDocument();
} );
it( 'should go to next step and show a back button', async () => {
const { queryByText, getByRole } = render(
<TourKit config={ config } />
);
userEvent.click( getByRole( 'button', { name: 'Next' } ) );
await waitFor( () =>
expect( queryByText( 'Step2' ) ).toBeInTheDocument()
);
expect( getByRole( 'button', { name: 'Back' } ) ).toBeInTheDocument();
} );
} );

View File

@ -0,0 +1,42 @@
/**
* External dependencies
*/
import {
Step,
Options,
Config,
TourStepRendererProps,
} from '@automattic/tour-kit';
export interface WooStep extends Step {
meta: {
heading: string | null;
descriptions: {
desktop: string | React.ReactElement | null;
mobile?: string | React.ReactElement | null;
};
primaryButton?: {
/** Set a text for the button. Default to "Done" for the last step and "Next" for the other steps */
text?: string;
/** Disable the button or not. Default to False */
isDisabled?: boolean;
};
};
/** Auto apply the focus state for the element. Default to null */
focusElement?: {
desktop?: string;
mobile?: string;
/** Iframe HTML selector. Default to null. If set, it will find the focus element in the iframe */
iframe?: string;
};
}
export type WooOptions = Options;
export interface WooConfig extends Omit< Config, 'renderers' | 'isMinimized' > {
steps: WooStep[];
options?: WooOptions;
}
export interface WooTourStepRendererProps extends TourStepRendererProps {
steps: WooStep[];
}

View File

@ -30,14 +30,14 @@
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"build": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
"start": "tsc --build --watch",
"prepack": "pnpm run clean && pnpm run build",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"test": "pnpm run build && pnpm run test:nobuild",
"test:nobuild": "jest --config ./jest.config.json",
"test": "jest --config ./jest.config.json",
"test-staged": "jest --bail --config ./jest.config.json --findRelatedTests"
},
"devDependencies": {

View File

@ -1,32 +0,0 @@
{
"root": "packages/js/csv-export",
"sourceRoot": "packages/js/csv-export/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/csv-export"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
}
}
}

View File

@ -33,14 +33,14 @@
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"build": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
"start": "tsc --build --watch",
"prepack": "pnpm run clean && pnpm run build",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"test": "pnpm run build && pnpm run test:nobuild",
"test:nobuild": "jest --config ./jest.config.json",
"test": "jest --config ./jest.config.json",
"test-staged": "jest --bail --config ./jest.config.json --findRelatedTests"
},
"devDependencies": {

View File

@ -1,32 +0,0 @@
{
"root": "packages/js/currency",
"sourceRoot": "packages/js/currency/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/currency"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
}
}
}

View File

@ -62,6 +62,7 @@
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"build": "pnpm run build:js && pnpm run build:css",
"build:js": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
@ -70,8 +71,7 @@
"prepack": "pnpm run clean && pnpm run build",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"test": "pnpm run build && pnpm run test:nobuild",
"test:nobuild": "jest --config ./jest.config.json",
"test": "jest --config ./jest.config.json",
"test-staged": "jest --bail --config ./jest.config.json --findRelatedTests"
},
"lint-staged": {

View File

@ -1,32 +0,0 @@
{
"root": "packages/js/customer-effort-score",
"sourceRoot": "packages/js/customer-effort-score/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/customer-effort-score"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
}
}
}

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add create product actions in products data store #33278

View File

@ -70,14 +70,14 @@
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"build": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
"start": "tsc --build --watch",
"prepack": "pnpm run clean && pnpm run build",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"test": "pnpm run build && pnpm run test:nobuild",
"test:nobuild": "jest --config ./jest.config.json",
"test": "jest --config ./jest.config.json",
"test-staged": "jest --bail --config ./jest.config.json --findRelatedTests"
},
"lint-staged": {

View File

@ -1,32 +0,0 @@
{
"root": "packages/js/data",
"sourceRoot": "packages/js/data/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/data"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
}
}
}

View File

@ -155,3 +155,4 @@ export interface WCDataSelector {
// Other exports
export { ActionDispatchers as PluginsStoreActions } from './plugins/actions';
export { ActionDispatchers as ProductsStoreActions } from './products/actions';

View File

@ -1,4 +1,6 @@
export enum TYPES {
CREATE_PRODUCT_ERROR = 'CREATE_PRODUCT_ERROR',
CREATE_PRODUCT_SUCCESS = 'CREATE_PRODUCT_SUCCESS',
GET_PRODUCT_SUCCESS = 'GET_PRODUCT_SUCCESS',
GET_PRODUCT_ERROR = 'GET_PRODUCT_ERROR',
GET_PRODUCTS_SUCCESS = 'GET_PRODUCTS_SUCCESS',

View File

@ -1,8 +1,20 @@
/**
* External dependencies
*/
import { apiFetch } from '@wordpress/data-controls';
import { DispatchFromMap } from '@automattic/data-stores';
/**
* Internal dependencies
*/
import TYPES from './action-types';
import { PartialProduct, ProductQuery } from './types';
import {
MutableProperties,
PartialProduct,
Product,
ProductQuery,
} from './types';
import { WC_PRODUCT_NAMESPACE } from './constants';
export function getProductSuccess( id: number, product: PartialProduct ) {
return {
@ -23,6 +35,25 @@ export function getProductError(
};
}
function createProductSuccess( id: number, product: PartialProduct ) {
return {
type: TYPES.CREATE_PRODUCT_SUCCESS as const,
id,
product,
};
}
export function createProductError(
query: Partial< Product >,
error: unknown
) {
return {
type: TYPES.CREATE_PRODUCT_ERROR as const,
query,
error,
};
}
export function getProductsSuccess(
query: Partial< ProductQuery >,
products: PartialProduct[],
@ -69,7 +100,25 @@ export function getProductsTotalCountError(
};
}
export function* createProduct( data: Pick< Product, MutableProperties > ) {
try {
const product: Product = yield apiFetch( {
path: WC_PRODUCT_NAMESPACE,
method: 'POST',
data,
} );
yield createProductSuccess( product.id, product );
return product;
} catch ( error ) {
yield createProductError( data, error );
throw error;
}
}
export type Actions = ReturnType<
| typeof createProductError
| typeof createProductSuccess
| typeof getProductSuccess
| typeof getProductError
| typeof getProductsSuccess
@ -77,3 +126,7 @@ export type Actions = ReturnType<
| typeof getProductsTotalCountSuccess
| typeof getProductsTotalCountError
>;
export type ActionDispatchers = DispatchFromMap< {
createProduct: typeof createProduct;
} >;

View File

@ -37,6 +37,7 @@ const reducer: Reducer< ProductState, Actions > = (
) => {
if ( payload && 'type' in payload ) {
switch ( payload.type ) {
case TYPES.CREATE_PRODUCT_SUCCESS:
case TYPES.GET_PRODUCT_SUCCESS:
const productData = state.data || {};
return {
@ -88,6 +89,7 @@ const reducer: Reducer< ProductState, Actions > = (
case TYPES.GET_PRODUCT_ERROR:
case TYPES.GET_PRODUCTS_ERROR:
case TYPES.GET_PRODUCTS_TOTAL_COUNT_ERROR:
case TYPES.CREATE_PRODUCT_ERROR:
return {
...state,
errors: {

View File

@ -77,7 +77,16 @@ export const getProductsError = (
return state.errors[ resourceName ];
};
export const getCreateProductError = (
state: ProductState,
query: ProductQuery
) => {
const resourceName = getProductResourceName( query );
return state.errors[ resourceName ];
};
export type ProductsSelectors = {
getCreateProductError: WPDataSelector< typeof getCreateProductError >;
getProducts: WPDataSelector< typeof getProducts >;
getProductsTotalCount: WPDataSelector< typeof getProductsTotalCount >;
getProductsError: WPDataSelector< typeof getProductsError >;

View File

@ -8,7 +8,7 @@ import {
getTotalProductCountResourceName,
} from '../utils';
import { Actions } from '../actions';
import { PartialProduct, ProductQuery } from '../types';
import { PartialProduct, ProductQuery, Product } from '../types';
const defaultState: ProductState = {
products: {},
@ -180,4 +180,34 @@ describe( 'products reducer', () => {
expect( state.errors[ resourceName ] ).toBe( error );
} );
it( 'should handle CREATE_PRODUCT_SUCCESS', () => {
const update: PartialProduct = {
id: 2,
name: 'Off the hook!',
status: 'draft',
};
const state = reducer( defaultState, {
type: TYPES.CREATE_PRODUCT_SUCCESS,
id: update.id,
product: update,
} );
expect( state.data[ 2 ].name ).toEqual( update.name );
expect( state.data[ 2 ].status ).toEqual( update.status );
} );
it( 'should handle CREATE_PRODUCT_ERROR', () => {
const query: Partial< Product > = { name: 'Donkey Sauce' };
const resourceName = getProductResourceName( query );
const error = 'Baaam!';
const state = reducer( defaultState, {
type: TYPES.GET_PRODUCTS_ERROR,
query,
error,
} );
expect( state.errors[ resourceName ] ).toBe( error );
} );
} );

View File

@ -40,6 +40,18 @@ export type Product<
sale_price: string;
};
export type MutableProperties =
| 'name'
| 'slug'
| 'type'
| 'status'
| 'featured'
| 'description'
| 'short_description'
| 'sku'
| 'regular_price'
| 'sale_price';
export type PartialProduct = Partial< Product > & Pick< Product, 'id' >;
export type ProductQuery<

View File

@ -48,14 +48,14 @@
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"build": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
"start": "tsc --build --watch",
"prepack": "pnpm run clean && pnpm run build",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"test": "pnpm run build && pnpm run test:nobuild",
"test:nobuild": "jest --config ./jest.config.json",
"test": "jest --config ./jest.config.json",
"test-staged": "jest --bail --config ./jest.config.json --findRelatedTests"
},
"lint-staged": {

View File

@ -1,32 +0,0 @@
{
"root": "packages/js/date",
"sourceRoot": "packages/js/date/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/date"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
}
}
}

View File

@ -36,9 +36,10 @@
"webpack-cli": "^3.3.12"
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"postinstall": "composer install"
"lint:fix": "eslint src --fix"
},
"lint-staged": {
"*.(t|j)s?(x)": [

View File

@ -1,14 +0,0 @@
{
"root": "packages/js/dependency-extraction-webpack-plugin",
"sourceRoot": "packages/js/dependency-extraction-webpack-plugin/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/dependency-extraction-webpack-plugin"
}
}
}
}

View File

@ -1,38 +0,0 @@
{
"root": "packages/js/e2e-core-tests/",
"sourceRoot": "packages/js/e2e-core-tests",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/e2e-core-tests"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"clean": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "clean"
}
},
"compile": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "compile"
}
},
"lint": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "lint"
}
}
}
}

View File

@ -38,7 +38,7 @@ Again, if you don't have shell access to your test site, through WP Admin ensure
3. You have an admin user set up (if their credentials differ from u/ `admin` and p/ `password` be sure to update `/plugins/woocommerce/tests/e2e/config/default.json`)
4. You have a customer user set up named 'Jane Smith'. This user should be a `subscriber` and again make sure their username and password are reflected in `/plugins/woocommerce/tests/e2e/config/default.json`.
You should then be able to run the e2e tests by running `pnpm nx test-e2e woocommerce`.
You should then be able to run the e2e tests by running `pnpm exec turbo run e2e --filter=woocommerce`.
### Test Sequencer Setup

View File

@ -1,80 +0,0 @@
{
"root": "packages/js/e2e-environment/",
"sourceRoot": "packages/js/e2e-environment/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/e2e-environment"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"lint": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "lint"
}
},
"clean": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "clean"
}
},
"compile": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "compile"
}
},
"docker-up": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "docker:up"
}
},
"docker-down": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "docker:down"
}
},
"docker-clear-all": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "docker:clear-all"
}
},
"docker-ssh": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "docker:ssh"
}
},
"test-e2e": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test:e2e"
}
},
"test-e2e-debug": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test:e2e-debug"
}
},
"test-e2e-dev": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test:e2e-dev"
}
}
}
}

View File

@ -0,0 +1,4 @@
Significance: patch
Type: add
Add tests to check usage button loading state

View File

@ -1,38 +0,0 @@
{
"root": "packages/js/e2e-utils/",
"sourceRoot": "packages/js/e2e-utils/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/e2e-utils"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"clean": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "clean"
}
},
"compile": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "compile"
}
},
"lint": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "lint"
}
}
}
}

View File

@ -129,6 +129,12 @@ const completeOnboardingWizard = async () => {
expect( continueButtons ).toHaveLength( 1 );
await continueButtons[ 0 ].click();
await expect( page ).toMatchElement(
'.woocommerce-usage-modal__actions button.is-secondary.is-busy'
);
await expect( page ).not.toMatchElement(
'.woocommerce-usage-modal__actions button.is-primary:disabled'
);
}
await page.waitForNavigation( { waitUntil: 'networkidle0' } );

View File

@ -36,9 +36,10 @@
"access": "public"
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"lint": "eslint ./rules ./configs",
"lint:fix": "eslint ./rules ./configs --fix",
"postinstall": "composer install"
"lint:fix": "eslint ./rules ./configs --fix"
},
"devDependencies": {
"@babel/core": "^7.17.5",

View File

@ -1,14 +0,0 @@
{
"root": "packages/js/eslint-plugin",
"sourceRoot": "packages/js/eslint-plugin/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/eslint-plugin"
}
}
}
}

View File

@ -75,6 +75,7 @@
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"build": "pnpm run build:js && pnpm run build:css",
"build:js": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
@ -83,8 +84,7 @@
"prepack": "pnpm run clean && pnpm run build",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"test": "pnpm run build && pnpm run test:nobuild",
"test:nobuild": "jest --config ./jest.config.json",
"test": "jest --config ./jest.config.json",
"test-staged": "jest --bail --config ./jest.config.json --findRelatedTests"
},
"lint-staged": {

View File

@ -1,32 +0,0 @@
{
"root": "packages/js/experimental",
"sourceRoot": "packages/js/experimental/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/experimental"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
}
}
}

View File

@ -48,14 +48,14 @@
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"build": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
"start": "tsc --build --watch",
"prepack": "pnpm run clean && pnpm run build",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"test": "pnpm run build && pnpm run test:nobuild",
"test:nobuild": "jest --config ./jest.config.json",
"test": "jest --config ./jest.config.json",
"test-staged": "jest --bail --config ./jest.config.json --findRelatedTests"
},
"lint-staged": {

View File

@ -1,32 +0,0 @@
{
"root": "packages/js/explat",
"sourceRoot": "packages/js/explat/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/explat"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
}
}
}

View File

@ -34,12 +34,26 @@ module.exports = {
],
},
webpackImporter: true,
additionalData:
'@use "sass:math";' +
'@import "_colors"; ' +
'@import "_variables"; ' +
'@import "_breakpoints"; ' +
'@import "_mixins"; ',
additionalData: ( content, loaderContext ) => {
const { resourcePath } = loaderContext;
if ( resourcePath.includes( '@automattic+' ) ) {
/*
* Skip adding additional data for @automattic/* packages to
* fix "SassError: @use rules must be written before any other rules."
* @automattic/* packages have included '@use "sass:math" and other necessary imports.
*/
return content;
}
return (
'@use "sass:math";' +
'@import "_colors"; ' +
'@import "_variables"; ' +
'@import "_breakpoints"; ' +
'@import "_mixins"; ' +
content
);
},
},
},
],

View File

@ -1,6 +0,0 @@
{
"root": "packages/js/internal-style-build",
"sourceRoot": "packages/js/internal-style-build/src",
"projectType": "library",
"targets": {}
}

View File

@ -40,14 +40,14 @@
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"build": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
"start": "tsc --build --watch",
"prepack": "pnpm run clean && pnpm run build",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"test": "pnpm run build && pnpm run test:nobuild",
"test:nobuild": "jest --config ./jest.config.json",
"test": "jest --config ./jest.config.json",
"test-staged": "jest --bail --config ./jest.config.json --findRelatedTests"
},
"devDependencies": {

View File

@ -1,32 +0,0 @@
{
"root": "packages/js/navigation",
"sourceRoot": "packages/js/navigation/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/navigation"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
}
}
}

View File

@ -28,14 +28,14 @@
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"build": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
"start": "tsc --build --watch",
"prepack": "pnpm run clean && pnpm run build",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"test": "pnpm run build && pnpm run test:nobuild",
"test:nobuild": "jest --config ./jest.config.json",
"test": "jest --config ./jest.config.json",
"test-staged": "jest --bail --config ./jest.config.json --findRelatedTests"
},
"devDependencies": {

View File

@ -1,32 +0,0 @@
{
"root": "packages/js/number",
"sourceRoot": "packages/js/number/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/number"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
}
}
}

View File

@ -56,6 +56,7 @@
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"build": "pnpm run build:js && pnpm run build:css",
"build:js": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",

View File

@ -1,32 +0,0 @@
{
"root": "packages/js/onboarding",
"sourceRoot": "packages/js/onboarding/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/onboarding"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
}
}
}

View File

@ -29,6 +29,7 @@
},
"scripts": {
"postinstall": "composer install",
"changelog": "composer exec -- changelogger",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"build": "tsc --build ./tsconfig.json ./tsconfig-cjs.json",
"start": "tsc --build --watch",

View File

@ -1,32 +0,0 @@
{
"root": "packages/js/tracks",
"sourceRoot": "packages/js/tracks/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/tracks"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
}
}
}

View File

@ -1,77 +0,0 @@
const pluginName = 'AsyncChunkSrcVersionParameterPlugin';
/**
* Inspired by: https://github.com/webpack/webpack/issues/8115#issuecomment-663902035.
*
* This plugin modifies the webpack bootstrap code generated by the plugin at
* webpack/lib/web/JsonpMainTemplatePlugin.js and the CSS chunk loading code generated
* by @automattic/mini-css-extract-plugin-with-rtl.
*
* It will rename the function jsonpScriptSrc generated by that to webpackJsonpScriptSrc
* and install a new version that checks a user provided variable containing a script
* version parameter to specify in async chunk URLs.
*
* The jsonpScriptSrc override is only for webpack 4 (tested with 4.43 and 4.44).
*
* Webpack 5 has official support for this https://github.com/webpack/webpack/pull/8462
* so it won't be necessary.
*
* It will also append the ?ver parameter to CSS chunk hrefs loaded by @automattic/mini-css-extract-plugin-with-rtl.
*/
class AsyncChunkSrcVersionParameterPlugin {
_applyMainTemplate( mainTemplate ) {
// Append script version to all async JS chunks loaded with jsonpScriptSrc().
mainTemplate.hooks.localVars.tap(
// Use stage 1 to ensure this executes after webpack/lib/web/JsonpMainTemplatePlugin.js.
{ name: pluginName, stage: 1 },
( source ) => {
if ( source.includes( 'function jsonpScriptSrc' ) ) {
const modSource = source.replace(
'function jsonpScriptSrc',
'function webpackJsonpScriptSrc'
);
return `${ modSource }
function jsonpScriptSrc(chunkId) {
var src = webpackJsonpScriptSrc(chunkId);
if ( window.wcAdminAssets && window.wcAdminAssets.version ) {
src += '?ver=' + window.wcAdminAssets.version;
}
return src;
}
`;
}
return source;
}
);
// Append script version to all async CSS chunks loaded by @automattic/mini-css-extract-plugin-with-rtl.
mainTemplate.hooks.requireEnsure.tap(
// Use stage 1 to ensure this executes after @automattic/mini-css-extract-plugin-with-rtl.
{ name: pluginName, stage: 1 },
( source ) => {
if (
source.includes( '// mini-css-extract-plugin CSS loading' )
) {
return source.replace(
'linkTag.href = fullhref;',
`linkTag.href = fullhref;
if ( window.wcAdminAssets && window.wcAdminAssets.version ) {
linkTag.href += '?ver=' + window.wcAdminAssets.version;
}`
);
}
return source;
}
);
}
apply( compiler ) {
compiler.hooks.thisCompilation.tap( pluginName, ( compilation ) => {
this._applyMainTemplate( compilation.mainTemplate );
} );
}
}
module.exports = AsyncChunkSrcVersionParameterPlugin;

View File

@ -22,6 +22,25 @@ import { WcAdminPaymentsGatewaysBannerSlot } from './payments/payments-settings-
// eslint-disable-next-line no-undef,camelcase
__webpack_public_path__ = global.wcAdminAssets.path;
// Modify webpack to append the ?ver parameter to JS chunk
// https://webpack.js.org/api/module-variables/#__webpack_get_script_filename__-webpack-specific
// eslint-disable-next-line no-undef,camelcase
const oldGetScriptFileNameFn = __webpack_get_script_filename__;
// eslint-disable-next-line no-undef,camelcase
__webpack_get_script_filename__ = ( chunk ) => {
const filename = oldGetScriptFileNameFn( chunk );
return `${ filename }?ver=${ window.wcAdminAssets.version }`;
};
// Modify webpack to append the ?ver parameter to CSS chunk hrefs generated by mini-css-extract-plugin
// eslint-disable-next-line no-undef,camelcase
const oldMinCssFn = __webpack_require__.miniCssF;
// eslint-disable-next-line no-undef,camelcase
__webpack_require__.miniCssF = ( chunkId ) => {
const filename = oldMinCssFn( chunkId );
return `${ filename }?ver=${ window.wcAdminAssets.version }`;
};
const appRoot = document.getElementById( 'root' );
const embeddedRoot = document.getElementById( 'woocommerce-embedded-root' );
const settingsGroup = 'wc_admin';

View File

@ -17,6 +17,7 @@ class UsageModal extends Component {
this.state = {
isLoadingScripts: false,
isRequestStarted: false,
selectedAction: null,
};
}
@ -131,7 +132,7 @@ class UsageModal extends Component {
acceptActionText = __( 'Yes, count me in!', 'woocommerce' ),
} = this.props;
const { isRequestStarted } = this.state;
const { isRequestStarted, selectedAction } = this.state;
const isBusy = isRequestStarted && isRequesting;
return (
@ -148,19 +149,23 @@ class UsageModal extends Component {
<div className="woocommerce-usage-modal__actions">
<Button
isSecondary
isBusy={ isBusy }
onClick={ () =>
this.updateTracking( { allowTracking: false } )
}
isBusy={ isBusy && selectedAction === 'dismiss' }
disabled={ isBusy && selectedAction === 'accept' }
onClick={ () => {
this.setState( { selectedAction: 'dismiss' } );
this.updateTracking( { allowTracking: false } );
} }
>
{ dismissActionText }
</Button>
<Button
isPrimary
isBusy={ isBusy }
onClick={ () =>
this.updateTracking( { allowTracking: true } )
}
isBusy={ isBusy && selectedAction === 'accept' }
disabled={ isBusy && selectedAction === 'dismiss' }
onClick={ () => {
this.setState( { selectedAction: 'accept' } );
this.updateTracking( { allowTracking: true } );
} }
>
{ acceptActionText }
</Button>

View File

@ -95,9 +95,12 @@ export const Products = () => {
) : (
isConfirmingLoadSampleProducts && (
<LoadSampleProductConfirmModal
onCancel={ () =>
setIsConfirmingLoadSampleProducts( false )
}
onCancel={ () => {
setIsConfirmingLoadSampleProducts( false );
recordEvent(
'tasklist_cancel_load_sample_products_click'
);
} }
onImport={ () => {
setIsConfirmingLoadSampleProducts( false );
loadSampleProduct();

View File

@ -165,5 +165,8 @@ describe( 'Products', () => {
userEvent.click( getByRole( 'button', { name: 'Cancel' } ) );
expect( queryByText( confirmModalText ) ).not.toBeInTheDocument();
expect( recordEvent ).toHaveBeenCalledWith(
'tasklist_cancel_load_sample_products_click'
);
} );
} );

View File

@ -189,9 +189,12 @@ export const Products = () => {
) : (
isConfirmingLoadSampleProducts && (
<LoadSampleProductConfirmModal
onCancel={ () =>
setIsConfirmingLoadSampleProducts( false )
}
onCancel={ () => {
setIsConfirmingLoadSampleProducts( false );
recordEvent(
'tasklist_cancel_load_sample_products_click'
);
} }
onImport={ () => {
setIsConfirmingLoadSampleProducts( false );
loadSampleProduct();

View File

@ -292,6 +292,9 @@ describe( 'Products', () => {
userEvent.click( getByRole( 'button', { name: 'Cancel' } ) );
expect( queryByText( confirmModalText ) ).not.toBeInTheDocument();
expect( recordEvent ).toHaveBeenCalledWith(
'tasklist_cancel_load_sample_products_click'
);
} );
it( 'should show spinner when layout experiment is loading', async () => {

View File

@ -103,8 +103,12 @@ export default function ProductTemplateModal( { onClose } ) {
).then(
( data ) => {
if ( data && data.id ) {
const additionalParams =
selectedTemplate === 'physical'
? '&spotlight=true'
: '';
const link = getAdminLink(
`post.php?post=${ data.id }&action=edit&wc_onboarding_active_task=products&tutorial=true`
`post.php?post=${ data.id }&action=edit&wc_onboarding_active_task=products&tutorial=true${ additionalParams }`
);
window.location = link;
}

View File

@ -0,0 +1,284 @@
/**
* External dependencies
*/
import { render, useEffect, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { TourKit, TourKitTypes } from '@woocommerce/components';
import qs from 'qs';
/**
* Internal dependencies
*/
import { useTmceIframeFocusStyle } from './use-tmce-iframe-focus-style';
import { useActiveEditorType } from './use-active-editor-type';
import {
bindEnableGuideModeClickEvent,
waitUntilElementTopNotChange,
} from './utils';
const getTourConfig = ( {
isExcerptEditorTmceActive,
isContentEditorTmceActive,
closeHandler,
}: {
isExcerptEditorTmceActive: boolean;
isContentEditorTmceActive: boolean;
closeHandler: () => void;
} ): TourKitTypes.WooConfig => {
return {
placement: 'bottom-start',
options: {
effects: {
spotlight: {
interactivity: {
enabled: true,
rootElementSelector: '#wpwrap',
},
},
arrowIndicator: true,
autoScroll: {
behavior: 'auto',
block: 'center',
},
},
popperModifiers: [
{
name: 'arrow',
options: {
padding: ( {
popper,
}: {
popper: { width: number };
} ) => {
return {
// Align the arrow to the left of the popper.
right: popper.width - 34,
};
},
},
},
],
},
steps: [
{
referenceElements: {
desktop: '#title',
},
focusElement: {
desktop: '#title',
},
meta: {
heading: __( 'Product name', 'woocommerce' ),
descriptions: {
desktop: __(
'Start typing your new product name here. This will be what your customers will see in your store.',
'woocommerce'
),
},
},
},
{
referenceElements: {
desktop: '#wp-content-wrap',
},
focusElement: {
iframe: isContentEditorTmceActive
? '#content_ifr'
: undefined,
desktop: isContentEditorTmceActive
? '#tinymce'
: '#wp-content-editor-container > .wp-editor-area',
},
meta: {
heading: __(
'Add your product description',
'woocommerce'
),
descriptions: {
desktop: __(
'Start typing your new product name here. Add your full product description here. Describe your product in detail.',
'woocommerce'
),
},
},
},
{
referenceElements: {
desktop: '#woocommerce-product-data',
},
focusElement: {
desktop: '#_regular_price',
},
meta: {
heading: __( 'Add your product data', 'woocommerce' ),
descriptions: {
desktop: __(
'Use the tabs to switch between sections and insert product details. Start by adding your product price.',
'woocommerce'
),
},
},
},
{
referenceElements: {
desktop: '#postexcerpt',
},
focusElement: {
iframe: isExcerptEditorTmceActive
? '#excerpt_ifr'
: undefined,
desktop: isExcerptEditorTmceActive
? '#tinymce'
: '#wp-excerpt-editor-container > .wp-editor-area',
},
meta: {
heading: __(
'Add your short product description',
'woocommerce'
),
descriptions: {
desktop: __(
'Type a quick summary for your product here. This will appear on the product page right under the product name.',
'woocommerce'
),
},
},
},
{
referenceElements: {
desktop: '#postimagediv',
},
focusElement: {
desktop: '#set-post-thumbnail',
},
meta: {
heading: __( 'Add your product image', 'woocommerce' ),
descriptions: {
desktop: __(
'Upload an image to your product here. Ideally a JPEG or PNG about 600 px wide or bigger. This image will be shown in your stores catalog.',
'woocommerce'
),
},
},
},
{
referenceElements: {
desktop: '#tagsdiv-product_tag',
},
focusElement: {
desktop: '#new-tag-product_tag',
},
meta: {
heading: __( 'Add your product tags', 'woocommerce' ),
descriptions: {
desktop: __(
'Add your product tags here. Tags are a method of labeling your products to make them easier for customers to find. For example, if you sell clothing, and you have a lot of cat prints, you could make a tag for “cat.”',
'woocommerce'
),
},
},
},
{
referenceElements: {
desktop: '#product_catdiv',
},
meta: {
heading: __( 'Add your product categories', 'woocommerce' ),
descriptions: {
desktop: __(
'Add your product categories here. Assign categories to your products to make them easier to browse through and find in your store.',
'woocommerce'
),
},
},
},
{
referenceElements: {
desktop: '#submitdiv',
},
focusElement: {
desktop: '#submitdiv',
},
meta: {
heading: __( 'Publish your product 🎉', 'woocommerce' ),
descriptions: {
desktop: __(
'Good work! Now you can publish your product to your store by hitting the “Publish” button or keep editing it.',
'woocommerce'
),
},
primaryButton: {
text: __( 'Keep editing', 'woocommerce' ),
},
},
},
],
closeHandler,
};
};
const ProductTour = () => {
const [ showTour, setShowTour ] = useState< boolean >( false );
const { isTmce: isContentEditorTmceActive } = useActiveEditorType( {
editorWrapSelector: '#wp-content-wrap',
} );
const { isTmce: isExcerptEditorTmceActive } = useActiveEditorType( {
editorWrapSelector: '#wp-excerpt-wrap',
} );
const { style: contentTmceIframeFocusStyle } = useTmceIframeFocusStyle( {
isActive: showTour && isContentEditorTmceActive,
iframeSelector: '#content_ifr',
} );
const { style: excerptTmceIframeFocusStyle } = useTmceIframeFocusStyle( {
isActive: showTour && isExcerptEditorTmceActive,
iframeSelector: '#excerpt_ifr',
} );
const tourConfig = getTourConfig( {
isContentEditorTmceActive,
isExcerptEditorTmceActive,
closeHandler: () => setShowTour( false ),
} );
useEffect( () => {
bindEnableGuideModeClickEvent( ( e ) => {
e.preventDefault();
setShowTour( true );
} );
const query = qs.parse( window.location.search.slice( 1 ) );
if ( query && query.tutorial === 'true' ) {
const intervalId = waitUntilElementTopNotChange(
tourConfig.steps[ 0 ].referenceElements?.desktop || '',
() => setShowTour( true ),
500
);
return () => clearInterval( intervalId );
}
// only run once
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [] );
if ( ! showTour ) {
return null;
}
return (
<>
<style>
{ contentTmceIframeFocusStyle }
{ excerptTmceIframeFocusStyle }
{ `.wp-editor-area:focus {
border: 1.5px solid #007CBA;
}` }
</style>
<TourKit config={ tourConfig } />
</>
);
};
const root = document.createElement( 'div' );
root.setAttribute( 'id', 'product-tour-root' );
render( <ProductTour />, document.body.appendChild( root ) );

View File

@ -0,0 +1,59 @@
/**
* External dependencies
*/
import { useEffect, useRef, useState } from '@wordpress/element';
/* TinyMCE or HTML (textarea) editor */
export type EditorType = 'tmce' | 'html';
export const useActiveEditorType = ( {
editorWrapSelector,
}: {
editorWrapSelector: string;
} ) => {
const editor = useRef( document.querySelector( editorWrapSelector ) );
if ( ! editor ) {
// eslint-disable-next-line no-console
console.warn( `Editor Wrap ${ editorWrapSelector } not found` );
}
const [ activeEditor, setActiveEditor ] = useState< EditorType >(
editor.current && editor.current.classList.contains( 'html-active' )
? 'html'
: 'tmce' // set to "tmce" if it's active or editor is not found
);
useEffect( () => {
const onClickEditorTab = ( e: MouseEvent ) => {
if ( e.target ) {
setActiveEditor(
( e.target as HTMLElement ).classList.contains(
'switch-html'
)
? 'html'
: 'tmce'
);
}
};
const tmceTab = document.querySelector< HTMLButtonElement >(
`${ editorWrapSelector } .switch-tmce`
);
tmceTab?.addEventListener( 'click', onClickEditorTab );
const htmlTab = document.querySelector< HTMLButtonElement >(
`${ editorWrapSelector } .switch-html`
);
htmlTab?.addEventListener( 'click', onClickEditorTab );
return () => {
tmceTab?.removeEventListener( 'click', onClickEditorTab );
htmlTab?.removeEventListener( 'click', onClickEditorTab );
};
}, [ editorWrapSelector ] );
return {
activeEditor,
isTmce: activeEditor === 'tmce',
isHtml: activeEditor === 'html',
};
};

View File

@ -0,0 +1,71 @@
/**
* External dependencies
*/
import { useEffect } from '@wordpress/element';
type addClassToIframeWhenChildFocusProps = {
iframeSelector: string;
childSelector: string;
className: string;
};
const addClassToIframeWhenChildFocus = ( {
iframeSelector,
childSelector,
className,
}: addClassToIframeWhenChildFocusProps ) => {
const iframe = document.querySelector< HTMLIFrameElement >(
iframeSelector
);
const innerDoc =
iframe?.contentDocument ||
( iframe?.contentWindow && iframe?.contentWindow.document );
if ( innerDoc ) {
const onFocus = () => iframe?.classList.add( className );
const onBlur = () => iframe?.classList.remove( className );
const child = innerDoc.querySelector< HTMLElement >( childSelector );
child?.addEventListener( 'focus', onFocus );
child?.addEventListener( 'blur', onBlur );
return () => {
child?.removeEventListener( 'focus', onFocus );
child?.removeEventListener( 'blur', onBlur );
};
}
return () => ( {} );
};
export const useTmceIframeFocusStyle = ( {
iframeSelector,
isActive,
}: {
iframeSelector: string;
isActive: boolean;
} ) => {
// Add a focus class to tmce iframe when editor is focused.
useEffect( () => {
if ( ! isActive ) {
return;
}
const clearIFrameEvent = addClassToIframeWhenChildFocus( {
iframeSelector: `${ iframeSelector }`,
childSelector: '#tinymce',
className: 'focus-within',
} );
return () => {
clearIFrameEvent();
};
}, [ isActive, iframeSelector ] );
return {
style: isActive
? `
${ iframeSelector }.focus-within {
border: 1.5px solid #007CBA;
}
`
: null,
};
};

View File

@ -0,0 +1,33 @@
// Wait until the initial element position is ready
export const waitUntilElementTopNotChange = (
elSelector: string,
cb: () => void,
pollMs: number
) => {
const initialElement = document.querySelector( elSelector );
let lastInitialElementTop = initialElement?.getBoundingClientRect().top;
const intervalId = setInterval( () => {
const top = initialElement?.getBoundingClientRect().top;
if ( lastInitialElementTop === top ) {
cb();
clearInterval( intervalId );
}
lastInitialElementTop = top;
}, pollMs );
return intervalId;
};
// Overwrite the default behavior of click event for the "Enable guided mode" button
export const bindEnableGuideModeClickEvent = (
onClick: EventListenerOrEventListenerObject
) => {
const enableGuideModeBtn = Array.from(
window.document.querySelectorAll( '.page-title-action' )
).find( ( el ) => el.textContent === 'Enable guided mode' );
if ( enableGuideModeBtn ) {
enableGuideModeBtn.addEventListener( 'click', onClick );
}
};

View File

@ -13,7 +13,7 @@ pnpm install
Build the example extension by running the pnpm script and passing the example name.
```bash
WC_EXT=<example> pnpm nx example woocommerce-admin
WC_EXT=<example> pnpm example --filter=@woocommerce/admin-library
```
Include the output plugin in your `.wp-env.json` and `.wp-env.override.json` and restart the WordPress instance. WooCommerce Analytics reports will now reflect the changes made by the example extension.

View File

@ -10,7 +10,7 @@ This feature is hidden behind a feature flag and can be turned on or off by visi
The fastest way to get started is by creating an example plugin from WooCommerce Admin. Enter the following command:
`WC_EXT=add-navigation-items pnpm nx example woocommerce-admin`
`WC_EXT=add-navigation-items pnpm example --filter=@woocommerce/admin-library`
This will create a new plugin that covers various features of the navigation and helps to register some intial items and categories within the new navigation menu. After running the command above, you can make edits directly to the files at `docs/examples/extensions/add-navigation-items` and they will be built and copied to your `wp-content/add-navigation-items` folder on save.

View File

@ -10,7 +10,7 @@ Gateway suggestions are retreived from a REST API and can be added via a remote
To quickly get started with an example plugin, run the following:
`WC_EXT=payment-gateway-suggestions pnpm nx example woocommerce-admin`
`WC_EXT=payment-gateway-suggestions pnpm example --filter=@woocommerce/admin-library`
This will create a new plugin that when activated will add two new gateway suggestions. The first is a simple gateway demonstrating how configuration fields can be pulled from the gateway class to create a configuration form. The second gateway shows a more customized approach via SlotFill.

View File

@ -42,7 +42,7 @@
"start": "pnpm run install-if-deps-outdated && cross-env WC_ADMIN_PHASE=development pnpm run build:packages && cross-env WC_ADMIN_PHASE=development pnpm run build:feature-config && concurrently \"cross-env WC_ADMIN_PHASE=development webpack --watch\" \"cross-env WC_ADMIN_PHASE=development pnpm run:packages -- start --parallel\"",
"start:package": "pnpm run:packages -- start --parallel",
"pretest": "pnpm run -s install-if-no-packages",
"test": "pnpm nx build @woocommerce/internal-js-tests && pnpm run test:client",
"test": "pnpm run test:client",
"test-staged": "pnpm run test:client -- --bail --findRelatedTests",
"test:client": "jest --config client/jest.config.js",
"test:debug": "node --inspect-brk ./node_modules/.bin/jest --config client/jest.config.js --watch --runInBand --no-cache",

View File

@ -1,43 +0,0 @@
{
"root": "plugins/woocommerce-admin/",
"sourceRoot": "plugins/woocommerce-admin",
"projectType": "application",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "plugins/woocommerce"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
"WC_ADMIN_PHASE=development pnpm nx clean woocommerce-admin",
"WC_ADMIN_PHASE=development pnpm nx dev woocommerce-admin",
"pnpm nx watch woocommerce-admin"
],
"parallel": false
}
},
"watch": {
"executor": "@nrwl/workspace:run-commands",
"options": {
"command": "pnpm nx client:watch woocommerce-admin"
}
},
"lint": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "lint"
}
}
}
}

View File

@ -13,9 +13,10 @@ const ForkTsCheckerWebpackPlugin = require( 'fork-ts-checker-webpack-plugin' );
/**
* Internal dependencies
*/
const AsyncChunkSrcVersionParameterPlugin = require( './chunk-src-version-param' );
const UnminifyWebpackPlugin = require( './unminify' );
const { webpackConfig: styleConfig } = require( '@woocommerce/internal-style-build' );
const {
webpackConfig: styleConfig,
} = require( '@woocommerce/internal-style-build' );
const WooCommerceDependencyExtractionWebpackPlugin = require( '../../packages/js/dependency-extraction-webpack-plugin/src/index' );
const NODE_ENV = process.env.NODE_ENV || 'development';
@ -54,6 +55,7 @@ const wpAdminScripts = [
'attributes-tracking',
'category-tracking',
'tags-tracking',
'product-tour',
];
const getEntryPoints = () => {
const entryPoints = {
@ -137,6 +139,9 @@ const webpackConfig = {
// Reduce bundle size by omitting Node crypto library.
// See https://github.com/woocommerce/woocommerce-admin/pull/5768
crypto: 'empty',
// Ignore fs, path to skip resolve errors for @automattic/calypso-config
fs: false,
path: false,
},
extensions: [ '.json', '.js', '.jsx', '.ts', '.tsx' ],
alias: {
@ -166,6 +171,9 @@ const webpackConfig = {
from: `../../packages/js/${ packageName }/build-style/*.css`,
to: `./${ packageName }/[name][ext]`,
noErrorOnMissing: true,
// Overwrites files already in compilation.assets to ensure we use the assets from the build-style.
// This is required for @woocommerce/component to use @automattic/* packages because scss styles from @automattic/* packages will be automatically generated by mini-css-extract-plugin with the same output name.
force: true,
} ) ),
} ),
@ -185,10 +193,6 @@ const webpackConfig = {
startYear: 2000,
} ),
process.env.ANALYZE && new BundleAnalyzerPlugin(),
// Adds the script version parameter to the chunk URLs for cache busting
// TODO: Partially replace with __webpack_get_script_filename__ in app with Webpack 5.x.
// The CSS chunk portion will need to remain, as it originates in MiniCssExtractPlugin.
new AsyncChunkSrcVersionParameterPlugin(),
// We only want to generate unminified files in the development phase.
WC_ADMIN_PHASE === 'development' &&
// Generate unminified files to load the unminified version when `define( 'SCRIPT_DEBUG', true );` is set in wp-config.

View File

@ -1,47 +0,0 @@
{
"root": "plugins/woocommerce-beta-tester/",
"sourceRoot": "plugins/woocommerce-beta-tester",
"projectType": "application",
"targets": {
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "plugins/woocommerce-beta-tester"
}
},
"composer-install": {
"executor": "@nrwl/workspace:run-commands",
"options": {
"command": "composer install",
"cwd": "plugins/woocommerce-beta-tester"
}
},
"composer-install-no-dev": {
"executor": "@nrwl/workspace:run-commands",
"options": {
"command": "composer install --no-dev",
"cwd": "plugins/woocommerce-beta-tester"
}
},
"composer-dump-autoload": {
"executor": "@nrwl/workspace:run-commands",
"options": {
"command": "composer dump-autoload",
"cwd": "plugins/woocommerce-beta-tester"
}
},
"lint-js": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "lint:js"
}
}
}
}

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