Compare commits
68 Commits
Author | SHA1 | Date |
---|---|---|
|
d2670bfcc7 | |
|
68abe08e4a | |
|
b44e8495ab | |
|
20152da6f4 | |
|
3e43ea9c52 | |
|
d9878e7a5a | |
|
e9a9d9e6dd | |
|
26d1a7b78f | |
|
0b1fe630b3 | |
|
fa1ce8f041 | |
|
e2d014a03c | |
|
24cbe3c071 | |
|
85a9c15083 | |
|
12ac84d2b7 | |
|
d38a6298b2 | |
|
813a4326d1 | |
|
d1827030ed | |
|
3a1f570cb5 | |
|
ec3180f86c | |
|
39facd1161 | |
|
1d1a40c074 | |
|
1b8c4596c0 | |
|
2629a30c1c | |
|
1b7631d4b8 | |
|
b56a11afec | |
|
4429576bdc | |
|
b6d63a55f2 | |
|
965f135474 | |
|
ee475f2e2a | |
|
4484f342a5 | |
|
d2ee8f0af6 | |
|
22852dd76d | |
|
e9ccb0b239 | |
|
4131728309 | |
|
4070a2e443 | |
|
393162059f | |
|
291f06be8c | |
|
df5e982181 | |
|
a872f419c6 | |
|
10623f82e2 | |
|
485bf919ed | |
|
389cf41a25 | |
|
e4fad93844 | |
|
235987623d | |
|
b9d5e0d5a8 | |
|
82c03f0424 | |
|
25011279c7 | |
|
ab6600dbd1 | |
|
5894dec4b8 | |
|
e517062528 | |
|
62787fd559 | |
|
2e9bb14e69 | |
|
54ca3776c7 | |
|
f60d8e3579 | |
|
b41f7ad08c | |
|
12f5114abd | |
|
e9d01f66b4 | |
|
3efc162842 | |
|
8ba6526ee9 | |
|
d1a68fe88d | |
|
871f58d342 | |
|
9260b29c37 | |
|
f3ee2eafc8 | |
|
f59169b6f7 | |
|
a984fb4b68 | |
|
5a3ff67f3a | |
|
3c87638693 | |
|
e2a090d7fe |
12
.env
12
.env
|
@ -12,5 +12,17 @@ MYSQL_DATABASE=${WORDPRESS_DB_NAME}
|
|||
MYSQL_USER=${WORDPRESS_DB_USER}
|
||||
MYSQL_PASSWORD=${WORDPRESS_DB_PASSWORD}
|
||||
WORDPRESS_DEBUG=true
|
||||
WORDPRESS_DEBUG_LOG=true
|
||||
FONTAWESOME_ENV=development
|
||||
MYSQL_ROOT_PASSWORD=somewordpress
|
||||
# This should be false by default.
|
||||
# It could be overridden in .env.local.
|
||||
# This pertains to the mod_security OWASP core rule set.
|
||||
ENABLE_MOD_SECURITY=false
|
||||
|
||||
# ENABLE_MOD_SECURITY=true, then the following
|
||||
# may be set to "true" to make exceptions to the usual mod_security rules,
|
||||
# allowing any requests on the font-awesome REST API routes that would
|
||||
# otherwise be blocked by the core rule set.
|
||||
#
|
||||
# ALLOW_ALL_REQUESTS_FOR_FONT_AWESOME=true
|
||||
|
|
|
@ -2,26 +2,26 @@ name: Jest
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- "**"
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
jest:
|
||||
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12'
|
||||
node-version: "18"
|
||||
check-latest: true
|
||||
|
||||
- name: Cache node_module
|
||||
id: node-modules-cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: admin/node_modules
|
||||
key: ${{ runner.os }}-node-modules-${{ hashFiles('admin/package-lock.json') }}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
name: PHP Composer
|
||||
name: PHP Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- "**"
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
|
@ -18,8 +19,7 @@ env:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
mysql:
|
||||
|
@ -34,30 +34,24 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
php: [7.1, 7.2, 7.3, 7.4]
|
||||
php: ["7.4", "8.0", "8.1", "8.2", "8.3"]
|
||||
wordpress: [latest]
|
||||
include:
|
||||
- php: 5.6
|
||||
wordpress: 5.2.5
|
||||
- php: 7.4
|
||||
- php: "8.3"
|
||||
wordpress: trunk
|
||||
- php: 8.0.14
|
||||
wordpress: 5.9-RC2
|
||||
# There are test errors in CI that aren't happening locally. What's different?
|
||||
# - php: 8.1
|
||||
# wordpress: 5.9-RC2
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: uopz
|
||||
|
||||
- name: Validate composer.json and composer.lock
|
||||
id: composer-lock
|
||||
run: |
|
||||
if [ ${{ matrix.php }} == 7.4 ]; then
|
||||
if [ ${{ matrix.php }} == '8.2' ]; then
|
||||
composer validate
|
||||
LOCK_FILE=composer.lock
|
||||
COMPOSER_FILE=composer.json
|
||||
|
@ -66,16 +60,16 @@ jobs:
|
|||
LOCK_FILE=composer-php${{ matrix.php }}.lock
|
||||
COMPOSER_FILE=composer-php${{ matrix.php }}.json
|
||||
fi
|
||||
echo "::set-output name=COMPOSER_LOCK_HASH::$(md5sum $LOCK_FILE | cut -d' ' -f1)"
|
||||
echo "::set-output name=COMPOSER_FILE::${COMPOSER_FILE}"
|
||||
echo "COMPOSER_LOCK_HASH=$(md5sum $LOCK_FILE | cut -d' ' -f1)" >> $GITHUB_OUTPUT
|
||||
echo "COMPOSER_FILE=${COMPOSER_FILE}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Resolve Cache Date
|
||||
id: cache-date
|
||||
run: echo "::set-output name=DATE::$(date +'%Y-%m-%d')"
|
||||
run: echo "DATE=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache Composer packages
|
||||
id: composer-cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: vendor
|
||||
key: ${{ runner.os }}-php-b-${{ steps.composer-lock.outputs.COMPOSER_LOCK_HASH }}
|
||||
|
@ -86,14 +80,14 @@ jobs:
|
|||
|
||||
- name: Cache APT Sources Daily
|
||||
id: apt-sources-cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /tmp/apt-sources-${{ steps.cache-date.outputs.DATE }}
|
||||
key: ${{ runner.os }}-apt-sources-${{ steps.cache-date.outputs.DATE }}
|
||||
|
||||
- name: Cache APT Packages Daily
|
||||
id: apt-packages-cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /tmp/apt-packages-${{ steps.cache-date.outputs.DATE }}
|
||||
key: ${{ runner.os }}-apt-packages-${{ steps.cache-date.outputs.DATE }}
|
||||
|
@ -122,44 +116,44 @@ jobs:
|
|||
- name: Resolve WordPress Version
|
||||
run: |
|
||||
curl -s https://api.wordpress.org/core/version-check/1.7/ > /tmp/wp-latest.json
|
||||
LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
|
||||
LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//' | head -1)
|
||||
if [ "${{ matrix.wordpress }}" == 'latest' ]; then
|
||||
VERSION=$LATEST_VERSION
|
||||
echo "::set-output name=WORDPRESS_VERSION::$VERSION"
|
||||
echo "::set-output name=WORDPRESS_VERSION_IS_TRUNK::0"
|
||||
echo "::set-output name=WORDPRESS_CORE_DIR::/tmp/$VERSION"
|
||||
echo "::set-output name=WP_TESTS_TAG::branches/$VERSION"
|
||||
echo "WORDPRESS_VERSION=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "WORDPRESS_VERSION_IS_TRUNK=0" >> $GITHUB_OUTPUT
|
||||
echo "WORDPRESS_CORE_DIR=/tmp/$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "WP_TESTS_TAG=branches/$VERSION" >> $GITHUB_OUTPUT
|
||||
elif [ "${{ matrix.wordpress }}" == 'trunk' ]; then
|
||||
VERSION=trunk-$(date +'%Y-%m-%d')
|
||||
echo "::set-output name=WORDPRESS_VERSION::$VERSION"
|
||||
echo "::set-output name=WORDPRESS_VERSION_IS_TRUNK::1"
|
||||
echo "::set-output name=WORDPRESS_CORE_DIR::/tmp/$VERSION"
|
||||
echo "::set-output name=WP_TESTS_TAG::trunk"
|
||||
echo "WORDPRESS_VERSION=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "WORDPRESS_VERSION_IS_TRUNK=1" >> $GITHUB_OUTPUT
|
||||
echo "WORDPRESS_CORE_DIR=/tmp/$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "WP_TESTS_TAG=trunk" >> $GITHUB_OUTPUT
|
||||
else
|
||||
VERSION=${{ matrix.wordpress }}
|
||||
echo "::set-output name=WORDPRESS_VERSION::$VERSION"
|
||||
echo "::set-output name=WORDPRESS_VERSION_IS_TRUNK::0"
|
||||
echo "::set-output name=WORDPRESS_CORE_DIR::/tmp/$VERSION"
|
||||
echo "WORDPRESS_VERSION=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "WORDPRESS_VERSION_IS_TRUNK=0" >> $GITHUB_OUTPUT
|
||||
echo "WORDPRESS_CORE_DIR=/tmp/$VERSION" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if [[ $VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then
|
||||
WP_BRANCH=${VERSION%\-*}
|
||||
echo "::set-output name=WP_TESTS_TAG::branches/$WP_BRANCH"
|
||||
echo "WP_TESTS_TAG=branches/$WP_BRANCH" >> $GITHUB_OUTPUT
|
||||
elif [[ $VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "::set-output name=WP_TESTS_TAG::branches/$VERSION"
|
||||
echo "WP_TESTS_TAG=branches/$VERSION" >> $GITHUB_OUTPUT
|
||||
elif [[ $VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
||||
if [[ $VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
|
||||
# version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
|
||||
echo "::set-output name=WP_TESTS_TAG::tags/${VERSION%??}"
|
||||
echo "WP_TESTS_TAG=tags/${VERSION%??}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "::set-output name=WP_TESTS_TAG::tags/$VERSION"
|
||||
echo "WP_TESTS_TAG=tags/$VERSION" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
elif [[ $VERSION == 'nightly' || $VERSION == 'trunk' ]]; then
|
||||
echo "::set-output name=WP_TESTS_TAG::trunk"
|
||||
echo "WP_TESTS_TAG=trunk" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "::set-output name=WP_TESTS_TAG::tags/$LATEST_VERSION"
|
||||
echo "WP_TESTS_TAG=tags/$LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
echo "::set-output name=WP_TESTS_DIR::/tmp/test/$VERSION"
|
||||
echo "WP_TESTS_DIR=/tmp/test/$VERSION" >> $GITHUB_OUTPUT
|
||||
id: wordpress-version
|
||||
|
||||
- name: Show WordPress Version
|
||||
|
@ -167,7 +161,7 @@ jobs:
|
|||
|
||||
- name: Cache WordPress Core Installation
|
||||
id: wordpress-cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.wordpress-version.outputs.WORDPRESS_CORE_DIR }}
|
||||
key: ${{ runner.os }}-wordpress-${{ steps.wordpress-version.outputs.WORDPRESS_VERSION }}') }}
|
||||
|
@ -189,7 +183,7 @@ jobs:
|
|||
|
||||
- name: Cache WordPress Test Installation
|
||||
id: wordpress-test-cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.wordpress-version.outputs.WP_TESTS_DIR }}
|
||||
key: ${{ runner.os }}-wordpress-test-${{ steps.wordpress-version.outputs.WORDPRESS_VERSION }}') }}
|
||||
|
@ -218,10 +212,10 @@ jobs:
|
|||
# only run the output tests on the newer versions of php and phpunit, cause
|
||||
# they're trickier
|
||||
run: |
|
||||
if [ "5.6" == ${{ matrix.php }} ] || [ "7.1" == ${{ matrix.php }} ] || [ "7.2" == ${{ matrix.php }} ] || [ "7.3" == ${{ matrix.php }} ]; then
|
||||
PHP_UNIT_ARGS="--exclude-group output"
|
||||
else
|
||||
if [ "8.3" == ${{ matrix.php }} ]; then
|
||||
PHP_UNIT_ARGS=""
|
||||
else
|
||||
PHP_UNIT_ARGS="--exclude-group output"
|
||||
fi
|
||||
|
||||
WP_PLUGIN_DIR="$(pwd)" \
|
||||
|
@ -230,6 +224,8 @@ jobs:
|
|||
vendor/bin/phpunit $PHP_UNIT_ARGS --group slow
|
||||
|
||||
- name: Run PHPUnit Loader Tests
|
||||
# 2/13/25: temporarily disabled. Passing in local dev against WP 6.7.2, failing in CI.
|
||||
if: false
|
||||
run: |
|
||||
WP_PLUGIN_DIR="$(pwd)" \
|
||||
COMPOSER_VENDOR_DIR="$WP_PLUGIN_DIR/vendor" \
|
||||
|
@ -272,7 +268,7 @@ jobs:
|
|||
|
||||
- name: Maybe run phpcs
|
||||
run: |
|
||||
if [ ${{ matrix.php }} == 7.4 ] && [ ${{ matrix.wordpress }} == latest ]; then
|
||||
if [ ${{ matrix.php }} == '8.2' ] && [ ${{ matrix.wordpress }} == latest ]; then
|
||||
composer phpcs
|
||||
echo
|
||||
echo "Skipping phpcs"
|
||||
|
|
|
@ -17,3 +17,10 @@ tmp/
|
|||
webpack-stats.html
|
||||
webpack-stats.json
|
||||
.phpunit.result.cache
|
||||
admin/src/playwright/.auth/
|
||||
admin/artifacts/
|
||||
admin/test-results/
|
||||
.npmrc
|
||||
.php-cs-fixer.cache
|
||||
.zed/
|
||||
.phpactor.json
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"$schema": "/phpactor.schema.json",
|
||||
"language_server": {
|
||||
"diagnostic_exclude_paths": ["**/wp-dist", "**/integrations/**/vendor"]
|
||||
},
|
||||
"indexer": {
|
||||
"exclude_patterns": ["**/wp-dist", "**/integrations/**/vendor"]
|
||||
},
|
||||
"php_code_sniffer.enabled": false,
|
||||
"php_code_sniffer.args": ["--standard=.phpcs.xml"]
|
||||
}
|
|
@ -16,10 +16,13 @@
|
|||
<arg name="extensions" value="php"/>
|
||||
<file>includes</file>
|
||||
<file>tests</file>
|
||||
<!-- TODO: maybe re-enable this. It's crashing phpcbf. -->
|
||||
<!--
|
||||
<file>font-awesome.php</file>
|
||||
-->
|
||||
<file>font-awesome-init.php</file>
|
||||
<file>block-editor/font-awesome-icon-block-init.php</file>
|
||||
<file>index.php</file>
|
||||
<file>v3shims.php</file>
|
||||
<file>defines.php</file>
|
||||
<file>admin/index.php</file>
|
||||
<file>admin/views/main.php</file>
|
|
@ -0,0 +1,6 @@
|
|||
block-editor/build
|
||||
icon-chooser/build
|
||||
classic-editor/build
|
||||
admin/build
|
||||
compat-js/build
|
||||
docs/
|
201
DEVELOPMENT.md
201
DEVELOPMENT.md
|
@ -9,6 +9,7 @@
|
|||
- [Optional Development Setup Steps](#optional-development-setup-steps)
|
||||
- [Run tests with phpunit](#run-tests-with-phpunit)
|
||||
* [Pass arguments to phpunit](#pass-arguments-to-phpunit)
|
||||
- [Run end-to-end tests with playwright](#run-end-to-end-tests-with-playwright)
|
||||
- [Use wp-cli within your Docker environment](#use-wp-cli-within-your-docker-environment)
|
||||
- [Run anything else within your Docker environment](#run-anything-else-within-your-docker-environment)
|
||||
* [Run a shell insider your Docker environment](#run-a-shell-inside-your-docker-environment)
|
||||
|
@ -18,12 +19,14 @@
|
|||
* [Main Options](#main-options)
|
||||
* [Releases Metadata Transient](#releases-metadata-transient)
|
||||
* [V3 Deprecation Warning](#v3-deprecation-warning)
|
||||
- [Managing web security rules](#managing-web-security-rules)
|
||||
- [Cut a Release](#cut-a-release)
|
||||
- [Run a Local Docs Server](#run-a-local-docs-server)
|
||||
- [Special Notes on plugin-sigma](#special-notes-on-plugin-sigma)
|
||||
- [Remote Debugging with VSCode](#remote-debugging-with-vscode)
|
||||
- [Redis Cache Setup](#redis-cache-setup)
|
||||
- [Analyze Webpack Bundle](#analyze-webpack-bundle)
|
||||
- [Test Against a WordPress Release Candidate](#test-against-a-wordpress-release-candidate)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
|
@ -38,7 +41,7 @@ to install any development dependencies inside the container.
|
|||
|
||||
The `integration` option does _not_ mount this plugin code in the container. Instead, it expects you
|
||||
to install the plugin. You could do so by uploading a zip file on the Add New Plugin page in
|
||||
WordPress admin: build a zip using `composer dist` or by downloading one from the [plugin's WordPress
|
||||
WordPress admin: build a zip using the release steps below or by downloading one from the [plugin's WordPress
|
||||
plugin directory entry](https://wordpress.org/plugins/font-awesome/). Or you could install directly
|
||||
from the WordPress plugin directory by searching for plugins by author "fontawesome".
|
||||
|
||||
|
@ -453,27 +456,6 @@ brew install composer
|
|||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>The WordPress 4 compat bundle</summary>
|
||||
For older versions of WordPress, we build and load this separate "compat-js" bundle. It includes some WordPress dependencies that are available at runtime on newer versions of WordPress, but which this plugin provides itself when it detects that
|
||||
it's being loaded on older versions of WordPress.
|
||||
|
||||
This bundle will probably not change very much, so it may not be necessary to rebuild at all.
|
||||
If you're doing development work only in WordPress 5, you can skip this altogether.
|
||||
|
||||
If you do need to update what's in this bundle, though, then you just build another
|
||||
static production build like this:
|
||||
|
||||
```
|
||||
$ cd compat-js
|
||||
$ npm install
|
||||
$ npm run build
|
||||
```
|
||||
|
||||
This will create `compat-js/build/compat.js`, which the plugin looks for and
|
||||
enqueues automatically when it detects that it's running under WordPress 4.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>If you have an older version of Docker or one that doesn't support host.docker.internal</summary>
|
||||
|
||||
|
@ -608,6 +590,53 @@ Everything before the `--` are the options do the `bin/phpunit` script, and ever
|
|||
to the `phpunit` command inside the container.
|
||||
</details>
|
||||
|
||||
# Run end-to-end tests with playwright
|
||||
|
||||
## Add tokens to `.env.local`
|
||||
|
||||
```
|
||||
API_TOKEN=YOUR_FA_API_TOKEN
|
||||
KIT_TOKEN=YOUR_KIT_TOKEN
|
||||
```
|
||||
|
||||
To run the end-to-end tests, you must have the WordPress environment running.
|
||||
For example, from the top-level directory, run this:
|
||||
|
||||
```bash
|
||||
bin/dev
|
||||
```
|
||||
|
||||
Leave that running in one terminal and do the following in a separate terminal.
|
||||
|
||||
Playwright must be also installed when initializing a local dev environment:
|
||||
```bash
|
||||
cd admin
|
||||
npx playwright install --with-deps
|
||||
```
|
||||
|
||||
Then, still in the `admin` directory, run tests on the terminal:
|
||||
```bash
|
||||
npx playwright test
|
||||
```
|
||||
|
||||
Or run the tests in the Playwright UI:
|
||||
```bash
|
||||
npx playwright test --ui
|
||||
```
|
||||
|
||||
Or in debug mode:
|
||||
```bash
|
||||
npx playwright test --debug
|
||||
```
|
||||
|
||||
See also [Playwright docs](https://playwright.dev/docs/intro).
|
||||
|
||||
## WordPress Version Caveat
|
||||
|
||||
The end-to-end tests may use features of WordPress that are not present in older versions, so their
|
||||
use on older versions may be limited. But within those limits, at least some of them are useful for
|
||||
running against older versions of WordPress to ensure compatibility.
|
||||
|
||||
# Use WP-CLI within your Docker environment
|
||||
|
||||
For example,
|
||||
|
@ -730,6 +759,36 @@ Remove it:
|
|||
$ bin/wp transient delete font-awesome-v3-deprecation-data
|
||||
```
|
||||
|
||||
# Managing web security rules
|
||||
|
||||
For the `latest` docker image, the latest release of the [OWASP core ruleset](https://coreruleset.org/) is installed by default,
|
||||
but _not_ enabled by default. This simulates what are probably common Web Application Firewall configurations for WordPress hosting providers.
|
||||
|
||||
By default, it merely audits. See the log in `/var/log/apache2/modsec_audit.log`.
|
||||
|
||||
To enable filtering--actually rejecting requests that exceed the rules' tolerances--edit your `.env.local`:
|
||||
|
||||
```
|
||||
ENABLE_MOD_SECURITY=true
|
||||
```
|
||||
|
||||
Note that this env var setting must be present in the environment when the docker container is created.
|
||||
So if you've already started a container, you'll need to stop and remove it, then change this env var,
|
||||
then start it back up.
|
||||
|
||||
You can watch the terminal where `apache2` is launched in the container. When `mod_security` is not enabled,
|
||||
it'll look like this:
|
||||
|
||||
```
|
||||
'apache2 -D FOREGROUND -D DEVELOPMENT'
|
||||
```
|
||||
|
||||
When `mod_security` is enabled, it'll look like this:
|
||||
|
||||
```
|
||||
'apache2 -D FOREGROUND -D DEVELOPMENT -D EnableModSecurity'
|
||||
```
|
||||
|
||||
# Cut a Release
|
||||
|
||||
## Running composer commands for the release
|
||||
|
@ -740,7 +799,7 @@ $ bin/wp transient delete font-awesome-v3-deprecation-data
|
|||
|
||||
3. Update the plugin version const in `includes/class-fontawesome.php`
|
||||
|
||||
4. Update the versions in `admin/package.json` and `compat-js/package.json`
|
||||
4. Update the versions in `admin/package.json`, `block-editor/package.json`, `classic-editor/package.json`, `icon-chooser/package.json`
|
||||
|
||||
5. Wait on changing the "Stable Tag" in `readme.txt` until after we've made the changes in the `svn` repo below.
|
||||
|
||||
|
@ -765,7 +824,7 @@ $ bin/wp transient delete font-awesome-v3-deprecation-data
|
|||
|
||||
- `git add docs` to stage them for commit (and eventually commit them)
|
||||
|
||||
7. Build production admin app and WordPress distribution layout into `wp-dist`
|
||||
7. Build production distribution archive.
|
||||
|
||||
```bash
|
||||
bin/composer dist
|
||||
|
@ -776,20 +835,7 @@ the default dev `latest` container, which you should be running via `bin/dev`.
|
|||
This will cause everything to be built inside the container, which will hopefully
|
||||
keep the built assets more consistent, regardless of the host environment.)
|
||||
|
||||
This will delete the previous build assets and produce the following:
|
||||
|
||||
`admin/build`: production build of the admin UI React app. This needs to be committed, so that it
|
||||
can be included in the composer package (which is really just a pull of this repo)
|
||||
|
||||
`compat-js/build`: production build of the compatibility JS bundled. This also needs to be committed.
|
||||
|
||||
8. Build the zip file
|
||||
|
||||
```bash
|
||||
bin/make-wp-dist-zip
|
||||
```
|
||||
|
||||
This builds the following:
|
||||
This will delete the previous build assets and produce:
|
||||
|
||||
`wp-dist/`: the contents of this directory contains everything that will be used in
|
||||
subsequent steps to both build an installable zip file, and to copy into the
|
||||
|
@ -804,13 +850,7 @@ a GitHub release.
|
|||
|
||||
9. Run through some manual acceptance testing
|
||||
|
||||
**WordPress 4.7, 4.8, 4.9**
|
||||
|
||||
For each of these 4.x environments, run and setup the corresponding integration container, like:
|
||||
```
|
||||
bin/integration 4.7
|
||||
bin/setup -c com.fontawesome.wordpress-4.7-integration
|
||||
```
|
||||
**WordPress 6.0**
|
||||
|
||||
Install and activate the Font Awesome plugin from the admin dashboard by uploading the `font-awesome.zip` file
|
||||
that was created in the previous step.
|
||||
|
@ -819,31 +859,16 @@ Run through the following, with the JavaScript console open, looking for any war
|
|||
|
||||
1. Load the plugin settings page.
|
||||
1. Change from Web Font to SVG and save.
|
||||
1. Create a new post (which will be in the Classic Editor)
|
||||
1. Install the Classic Editor plugin
|
||||
1. Create a new post with the Classic Editor
|
||||
1. Click the "Add Font Awesome" button
|
||||
1. Search for something, and click to insert an icon from the results
|
||||
|
||||
**WordPress 5.0**
|
||||
|
||||
Setup the integration environment as above, but also do the following editor
|
||||
integration tests intead:
|
||||
|
||||
1. Install the Classic Editor plugin
|
||||
1. Create a post with the Classic Editor
|
||||
1. Click the "Add Font Awesome" media button
|
||||
1. Search for something, and click to insert an icon from the results
|
||||
1. Create a new post, switching to the Gutenberg / Block Editor
|
||||
1. Expect to see a compatibility warning that the Icon Chooser is not enabled,
|
||||
but otherwise expect the Block Editor to function normally
|
||||
|
||||
**WordPress 5.4**
|
||||
|
||||
Setup the integration environment as above. Do the same tests as on 5.0, but also
|
||||
expect the Icon Chooser to be enabled within the Block Editor:
|
||||
|
||||
1. Create a post with the Block Editor
|
||||
1. Activate the Icon Chooser
|
||||
1. Add an icon block to a post
|
||||
1. Search for something, and click to insert an icon from the results
|
||||
1. style the icon
|
||||
1. Add a RichText icon (inline)
|
||||
1. style that too
|
||||
|
||||
**WordPress latest**
|
||||
|
||||
|
@ -1062,7 +1087,7 @@ $ cd ..
|
|||
10. Copy plugin directory assets and wp-dist layout into `wp-svn/trunk`
|
||||
|
||||
```bash
|
||||
bin/dist2trunk
|
||||
bin/dist-to-trunk
|
||||
```
|
||||
|
||||
This script will just `rm *` anything under `wp-svn/trunk/*` and `wp-svn/assets/*` to make sure that if the new dist
|
||||
|
@ -1120,15 +1145,7 @@ password. After the first `svn ci` caches the credentials, you probably won't ne
|
|||
|
||||
[See also tips on using SVN with WordPress Plugins](https://developer.wordpress.org/plugins/wordpress-org/how-to-use-subversion/#editing-existing-files).
|
||||
|
||||
13. OPTIONAL: Test Installation from WordPress Plugins Directory
|
||||
|
||||
Once the trunk has been committed above, this new version is available as the current "Development Version",
|
||||
available for download as a ZIP file from the plugin's [Advanced View](https://wordpress.org/plugins/font-awesome/advanced/). (See the dropdown under the header: "Please select a specific version to download.")
|
||||
|
||||
To try it out, you could start up a clean integration environment from this repo, and then
|
||||
install the plugin using that downloaded ZIP file.
|
||||
|
||||
14. Create the new svn release tag
|
||||
13. Create the new svn release tag
|
||||
|
||||
First, make sure `svn stat` is clean. We want to make sure that the trunk is all committed and clean before we take a
|
||||
snapshot of it for the release tag.
|
||||
|
@ -1139,6 +1156,18 @@ This will snapshot `trunk` as a new release tag. Replace the example tag name wi
|
|||
svn cp trunk tags/42.1.2
|
||||
```
|
||||
|
||||
```bash
|
||||
svn ci -m 'Tag release 42.1.2'
|
||||
```
|
||||
|
||||
14. OPTIONAL: Test Installation from WordPress Plugins Directory
|
||||
|
||||
Once the trunk has been committed above, this new version is available as the current "Development Version",
|
||||
available for download as a ZIP file from the plugin's [Advanced View](https://wordpress.org/plugins/font-awesome/advanced/). (See the dropdown under the header: "Please select a specific version to download.")
|
||||
|
||||
To try it out, you could start up a clean integration environment from this repo, and then
|
||||
install the plugin using that downloaded ZIP file.
|
||||
|
||||
15. Update `Stable tag` and `Tested up to` tags in `readme.txt`
|
||||
|
||||
We've now got three copies of `readme.txt` that should all be updated with new tag values:
|
||||
|
@ -1180,15 +1209,11 @@ If you want to preview the built docs with a web server, first build the docs:
|
|||
bin/phpdoc
|
||||
```
|
||||
|
||||
Then go into the `docsrv` directory and run the doc server:
|
||||
Then go into the `docs` directory and run:
|
||||
```
|
||||
cd docsrv
|
||||
npm install
|
||||
node index.js
|
||||
npx serve
|
||||
```
|
||||
|
||||
Point a web browser at `http://localhost:3000`.
|
||||
|
||||
# Special Notes on plugin-sigma
|
||||
|
||||
`plugin-sigma` demonstrates how a third-party plugin developer could include this Font Awesome plugin as a composer
|
||||
|
@ -1271,7 +1296,7 @@ wp --allow-root core update --version=5.4 /tmp/wordpress-5.4-latest.zip
|
|||
|
||||
# Analyze Webpack Bundle
|
||||
|
||||
The webpack configs for both `admin/` and `compat-js/` include the `BundleAnalyzerPlugin`,
|
||||
The webpack configs for the `admin/` JavaScript bundle includes the `BundleAnalyzerPlugin`,
|
||||
which produces a corresponding `webpack-stats.html` file in the corresponding
|
||||
directory on each build.
|
||||
|
||||
|
@ -1289,3 +1314,13 @@ See [guide here](https://wordpress.org/support/article/create-a-network/)
|
|||
```
|
||||
wp --allow-root eval 'require_once "wp-content/plugins/font-awesome/includes/class-fontawesome-deactivator.php"; use FortAwesome\FontAwesome_Deactivator; define("WP_NETWORK_ADMIN", true); FontAwesome_Deactivator::uninstall();'
|
||||
```
|
||||
|
||||
# Test Against a WordPress Release Candidate
|
||||
|
||||
Probably the easiest is to just use `bin/dev` to run the currently-latest release,
|
||||
and then use `bin/env /bin/bash` to shell into that container and use the WP CLI
|
||||
to update the release.
|
||||
|
||||
For example, when WordPress was at `RC3` for version 6.3, this installed as expected:
|
||||
|
||||
`wp --allow-root core update --version=6.3-RC3`
|
||||
|
|
|
@ -733,14 +733,14 @@ Once the site owner enables Pro, `fa()->pro()` will be `true` and your code can
|
|||
then rely on the presence of Font Awesome Pro for the version indicated by
|
||||
`fa()->version()`.
|
||||
|
||||
(See the [PHP API docs](https://fortawesome.github.io/wordpress-fontawesome/index.html) for how to resolve the symbolic
|
||||
(See the [PHP API docs](https://fortawesome.github.io/wordpress-fontawesome/) for how to resolve the symbolic
|
||||
`"latest"` version as a concrete version like `"5.12.0"`.)
|
||||
|
||||
# Query the Font Awesome GraphQL API
|
||||
|
||||
The Font Awesome [GraphQL API](https://fontawesome.com/docs/apis/graphql/get-started) allows you to query and search icon metadata.
|
||||
|
||||
See also documentation in PHP API on the [`FontAwesome::query()`](https://fortawesome.github.io/wordpress-fontawesome/classes/FortAwesome.FontAwesome.html#method_query) method.
|
||||
See also documentation in PHP API on the [`FontAwesome::query()`](https://fortawesome.github.io/wordpress-fontawesome/) method.
|
||||
|
||||
## public scope queries on api.fontawesome.com
|
||||
|
||||
|
@ -755,7 +755,8 @@ fetch(
|
|||
'https://api.fontawesome.com',
|
||||
{
|
||||
method: 'POST',
|
||||
body: 'query { release(version:"5.12.0") { icons { id } } }'
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: '{"query": "query Icons($ver: String!) { release(version:$ver) { icons { id } } }", "variables": { "ver": "6.x" } }'
|
||||
}
|
||||
)
|
||||
.then(response => response.ok ? response.json() : null)
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"bracketSameLine": false,
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"printWidth": 160,
|
||||
"quoteProps": "consistent",
|
||||
"semi": false,
|
||||
"singleAttributePerLine": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "none",
|
||||
"useTabs": false
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,279 +0,0 @@
|
|||
.Ihb3kyONjVgEWV0zB0vfxQ\=\= {
|
||||
margin-right: 20px;
|
||||
padding: 1rem 2rem 2rem 2rem;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1024px) {
|
||||
.Ihb3kyONjVgEWV0zB0vfxQ\=\= {
|
||||
max-width: 1000px;
|
||||
}
|
||||
}
|
||||
|
||||
.ToMOGjAmxSPs6D\+glWGx9A\=\= {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
._3edKMOIt9iftk0Zz-7ia9w\=\= {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
div._3edKMOIt9iftk0Zz-7ia9w\=\=.NbqVQtCU5W3ihwasrYexfA\=\= {
|
||||
display: flex;
|
||||
margin: 1em;
|
||||
background-color: #fda09a;
|
||||
border-radius: 5px;
|
||||
max-width: 450px;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
div._3edKMOIt9iftk0Zz-7ia9w\=\=.NbqVQtCU5W3ihwasrYexfA\=\= .gEl1fl\+2lk74ueiQz5cPxQ\=\= {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
div._3edKMOIt9iftk0Zz-7ia9w\=\=.NbqVQtCU5W3ihwasrYexfA\=\= .vumGDcuTrv0Ekcc\+McKiXw\=\= {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
._3edKMOIt9iftk0Zz-7ia9w\=\= .vumGDcuTrv0Ekcc\+McKiXw\=\= {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
._3edKMOIt9iftk0Zz-7ia9w\=\=._7IGgJmOfwN1O\+c0smUgA9Q\=\= .b1NRGX9AXkY1BfJ1MCfPTw\=\= {
|
||||
color: green;
|
||||
}
|
||||
|
||||
h2._3hBTOhCdvQjibQScAbzMIQ\=\= {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h3._3hBTOhCdvQjibQScAbzMIQ\=\= {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
._0XZF4B-SzNg4vm8Dl0F7TA\=\= {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.YGhcFGkqFAeqpY9iNYq9Sw\=\= th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button.mQ\+dGgo7ePYMdFVM7UJy3Q\=\= {
|
||||
border: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.LZqubosol4XlcTmVPXrgwA\=\= {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
._92G6m9T1MVtvbrtbN0ztew\=\= {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
button.vKD-ffoQma2PtYJ6syJLXA\=\= {
|
||||
border: 1px solid #0064B1;
|
||||
border-bottom: 4px solid #0064B1;
|
||||
border-radius: 3px;
|
||||
padding: .7em 1.5em;
|
||||
background: #008DED;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 1.4em;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button.vKD-ffoQma2PtYJ6syJLXA\=\=[disabled] {
|
||||
border: 1px solid #F8F9FA;
|
||||
background: #F8F9FA;
|
||||
color: #008DED;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
button .jRDHFr0fk9vh0tmPg3yyNA\=\= {
|
||||
display: inline-block;
|
||||
min-width: 3.2em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.BHff-dIr\+7jxh1slKud1UA\=\= {
|
||||
background-color: #fdfdf3;
|
||||
max-width: 600px;
|
||||
padding: 1.5em;
|
||||
border-radius: 5px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
._8sv48aq5xq1UY1HM-IXXWw\=\= {
|
||||
border: 0;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1024px) {
|
||||
.XWyrhxEjrFCimjviedIRKg\=\= {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.KIG-iO8JlK18PTTkxmFFfQ\=\= {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.wbV6cqcB6HeXauGMWUOUJw\=\= {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.PWf16KXgVsL5DasX-69r\+w\=\= {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.BFnd\+XqC\+F5AvKCZ6eMOOA\=\= .b1NRGX9AXkY1BfJ1MCfPTw\=\= {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.BFnd\+XqC\+F5AvKCZ6eMOOA\=\=.x86R9\+0TG6mMWrDQqDWMxQ\=\= .b1NRGX9AXkY1BfJ1MCfPTw\=\=, ._8fwuockVscy-LVkmd5sRrg\=\= {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.BFnd\+XqC\+F5AvKCZ6eMOOA\=\=._4ywMQ6iToIUtlBzG0klUZQ\=\= .b1NRGX9AXkY1BfJ1MCfPTw\=\=, .n2ieOzL8DYqXeYvR4dnlBQ\=\= {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.BFnd\+XqC\+F5AvKCZ6eMOOA\=\=.BHff-dIr\+7jxh1slKud1UA\=\= .b1NRGX9AXkY1BfJ1MCfPTw\=\=, .Kc0JjWetOt7bzIP5T5F-3g\=\= {
|
||||
color: #b7b700;
|
||||
}
|
||||
|
||||
.cdItXesO30xESmpowZCWVA\=\= {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.XrgwDjPx-AobEiR9810sug\=\= ~ label .wfGA8rTfLXMNYeed\+7P0mg\=\=, .YGOg\+3jg-Q6uUjZrsKJJBw\=\= ~ label .wfGA8rTfLXMNYeed\+7P0mg\=\= {
|
||||
display:none;
|
||||
opacity:0;
|
||||
}
|
||||
|
||||
.XrgwDjPx-AobEiR9810sug\=\=:checked ~ label .wfGA8rTfLXMNYeed\+7P0mg\=\=, .YGOg\+3jg-Q6uUjZrsKJJBw\=\=:checked ~ label .wfGA8rTfLXMNYeed\+7P0mg\=\={
|
||||
display:block;
|
||||
opacity:1.0;
|
||||
color: #228be6;
|
||||
}
|
||||
|
||||
.XrgwDjPx-AobEiR9810sug\=\=:checked ~ label .dxKTDsQBZGG-O07iRE7TNg\=\=, .YGOg\+3jg-Q6uUjZrsKJJBw\=\=:checked ~ label .dxKTDsQBZGG-O07iRE7TNg\=\={
|
||||
display:none;
|
||||
opacity:0;
|
||||
}
|
||||
|
||||
.YGOg\+3jg-Q6uUjZrsKJJBw\=\=:checked ~ label .AX07i\+p1n9K\+g4HYk3mvOg\=\= {
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.YGOg\+3jg-Q6uUjZrsKJJBw\=\=:checked ~ label .AX07i\+p1n9K\+g4HYk3mvOg\=\= a {
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.YGOg\+3jg-Q6uUjZrsKJJBw\=\=:checked ~ label .AX07i\+p1n9K\+g4HYk3mvOg\=\= a:hover,
|
||||
.YGOg\+3jg-Q6uUjZrsKJJBw\=\= ~ label .AX07i\+p1n9K\+g4HYk3mvOg\=\= a:hover {
|
||||
color: #228be6;
|
||||
-webkit-text-decoration-color: initial;
|
||||
text-decoration-color: initial;
|
||||
}
|
||||
|
||||
|
||||
.d7wuKQTkcJufIbd\+gVhKnw\=\= {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.F6JwARlyXPrf\+kIygaN8dQ\=\= {
|
||||
padding: 1rem;
|
||||
margin: 1rem 1rem 1rem 0rem;
|
||||
border: 1px dotted grey;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.v2APGCcZUAaU68TnPHhvxw\=\= {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: no-wrap;
|
||||
align-items: stretch;
|
||||
background-color: #E4F6FF;
|
||||
color: rgb(73, 80, 87);
|
||||
border-radius: 0.25rem;
|
||||
margin-top: 1rem;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.syPwBWS1kp-zUKz4hcgcXg\=\= {
|
||||
color: #008BED;
|
||||
border-top-left-radius: 0.25rem;
|
||||
border-bottom-left-radius: 0.25rem;
|
||||
padding: .55rem .25rem .5rem .75rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.ptjLX6BwJtUff-P6OkZBiA\=\= {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.2rem;
|
||||
font-size: .8rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.VAB708TLB4qhUVdnQGAxJA\=\= {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: .5rem 1rem .75rem .25rem;
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
.VAB708TLB4qhUVdnQGAxJA\=\= p {
|
||||
margin-top: 0;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
.VAB708TLB4qhUVdnQGAxJA\=\= svg {
|
||||
font-size: .7rem;
|
||||
}
|
||||
|
||||
.VAB708TLB4qhUVdnQGAxJA\=\= ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.VAB708TLB4qhUVdnQGAxJA\=\= li {
|
||||
display: inline-block;
|
||||
padding-right: 1rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.CIIJrcA\+PLxU-W4xIVozXw\=\= {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.v2APGCcZUAaU68TnPHhvxw\=\= button {
|
||||
color: #0073aa;
|
||||
}
|
||||
|
||||
.v2APGCcZUAaU68TnPHhvxw\=\= button:hover {
|
||||
color: #00a0d2;
|
||||
}
|
||||
|
||||
/* type: warning */
|
||||
.iAbTOYj3VuCpNr1NEwmL4g\=\= {
|
||||
background: rgb(255, 249, 219);
|
||||
}
|
||||
|
||||
.iAbTOYj3VuCpNr1NEwmL4g\=\= .syPwBWS1kp-zUKz4hcgcXg\=\= {
|
||||
color: rgb(250, 176, 7);
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -1 +0,0 @@
|
|||
(window.webpackJsonp_font_awesome_admin=window.webpackJsonp_font_awesome_admin||[]).push([[13],{289:function(n,o,w){"use strict";w.r(o)}}]);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
|||
(window.webpackJsonp_font_awesome_admin=window.webpackJsonp_font_awesome_admin||[]).push([[16],{281:function(i,t,s){"use strict";s.r(t),s.d(t,"fa_icon",(function(){return o}));var e=s(184),n=s(206);let o=class{constructor(i){Object(e.j)(this,i),this.pro=!1,this.loading=!1}componentWillLoad(){if(this.iconUpload)return void(this.iconDefinition={prefix:"fak",iconName:this.iconUpload.name,icon:[parseInt(""+this.iconUpload.width),parseInt(""+this.iconUpload.height),[],this.iconUpload.unicode.toString(16),this.iconUpload.path]});if(this.icon)return void(this.iconDefinition=this.icon);if(!this.svgApi)return void console.error(n.a+": fa-icon: svgApi prop is needed but is missing",this);if(!this.stylePrefix||!this.name)return void console.error(n.a+": fa-icon: the 'stylePrefix' and 'name' props are needed to render this icon but not provided.",this);const{findIconDefinition:i}=this.svgApi,t=i&&i({prefix:this.stylePrefix,iconName:this.name});if(t)return void(this.iconDefinition=t);if(!this.pro)return void console.error(n.a+": fa-icon: 'pro' prop is false but no free icon is available",this);if(!this.svgFetchBaseUrl)return void console.error(n.a+": fa-icon: 'svgFetchBaseUrl' prop is absent but is necessary for fetching icon",this);if(!this.kitToken)return void console.error(n.a+": fa-icon: 'kitToken' prop is absent but is necessary for accessing icon",this);this.loading=!0;const s=`${this.svgFetchBaseUrl}/${n.c[this.stylePrefix]}/${this.name}.svg?token=${this.kitToken}`,e=n.k.get(this,"svgApi.library");"function"==typeof this.getUrlText?this.getUrlText(s).then(i=>{const t={iconName:this.name,prefix:this.stylePrefix,icon:Object(n.l)(i)};e&&e.add(t),this.iconDefinition=Object.assign({},t)}).catch(i=>{console.error(n.a+": fa-icon: failed when using 'getUrlText' to fetch icon",i,this)}).finally(()=>{this.loading=!1}):console.error(n.a+": fa-icon: 'getUrlText' prop is absent but is necessary for fetching icon",this)}buildSvg(i,t){if(!i)return;const[s,o,,,r]=n.k.get(i,"icon",[]),c=["svg-inline--fa"];this.class&&c.push(this.class),t&&c.push(t),this.size&&c.push("fa-"+this.size);const a=c.join(" ");return Array.isArray(r)?Object(e.h)("svg",{class:a,xmlns:"http://www.w3.org/2000/svg",viewBox:`0 0 ${s} ${o}`},Object(e.h)("path",{fill:"currentColor",class:"fa-primary",d:r[1]}),Object(e.h)("path",{fill:"currentColor",class:"fa-secondary",d:r[0]})):Object(e.h)("svg",{class:a,xmlns:"http://www.w3.org/2000/svg",viewBox:`0 0 ${s} ${o}`},Object(e.h)("path",{fill:"currentColor",d:r}))}render(){return this.iconDefinition?this.buildSvg(this.iconDefinition):Object(e.h)(e.f,null)}};o.style=""}}]);
|
|
@ -1 +0,0 @@
|
|||
(window.webpackJsonp_font_awesome_admin=window.webpackJsonp_font_awesome_admin||[]).push([[17],{279:function(t,e,s){"use strict";s.r(e),s.d(e,"scopeCss",(function(){return j}));const o=")(?:\\(((?:\\([^)(]*\\)|[^)(]*)+?)\\))?([^,{]*)",r=new RegExp("(-shadowcsshost"+o,"gim"),c=new RegExp("(-shadowcsscontext"+o,"gim"),n=new RegExp("(-shadowcssslotted"+o,"gim"),l=/-shadowcsshost-no-combinator([^\s]*)/,a=[/::shadow/g,/::content/g],i=/-shadowcsshost/gim,h=/:host/gim,p=/::slotted/gim,d=/:host-context/gim,u=/\/\*\s*[\s\S]*?\*\//g,m=/\/\*\s*#\s*source(Mapping)?URL=[\s\S]+?\*\//g,g=/(\s*)([^;\{\}]+?)(\s*)((?:{%BLOCK%}?\s*;?)|(?:\s*;))/g,w=/([{}])/g,f=/(^.*?[^\\])??((:+)(.*)|$)/,_=(t,e)=>{const s=x(t);let o=0;return s.escapedString.replace(g,(...t)=>{const r=t[2];let c="",n=t[4],l="";n&&n.startsWith("{%BLOCK%")&&(c=s.blocks[o++],n=n.substring("%BLOCK%".length+1),l="{");const a=e({selector:r,content:c});return`${t[1]}${a.selector}${t[3]}${l}${a.content}${n}`})},x=t=>{const e=t.split(w),s=[],o=[];let r=0,c=[];for(let t=0;t<e.length;t++){const n=e[t];"}"===n&&r--,r>0?c.push(n):(c.length>0&&(o.push(c.join("")),s.push("%BLOCK%"),c=[]),s.push(n)),"{"===n&&r++}return c.length>0&&(o.push(c.join("")),s.push("%BLOCK%")),{escapedString:s.join(""),blocks:o}},$=(t,e,s)=>t.replace(e,(...t)=>{if(t[2]){const e=t[2].split(","),o=[];for(let r=0;r<e.length;r++){const c=e[r].trim();if(!c)break;o.push(s("-shadowcsshost-no-combinator",c,t[3]))}return o.join(",")}return"-shadowcsshost-no-combinator"+t[3]}),b=(t,e,s)=>t+e.replace("-shadowcsshost","")+s,O=(t,e,s)=>e.indexOf("-shadowcsshost")>-1?b(t,e,s):t+e+s+", "+e+" "+t+s,S=(t,e)=>t.replace(f,(t,s="",o,r="",c="")=>s+e+r+c),W=(t,e,s,o,r)=>_(t,t=>{let r=t.selector,c=t.content;return"@"!==t.selector[0]?r=((t,e,s,o)=>t.split(",").map(t=>o&&t.indexOf("."+o)>-1?t.trim():((t,e)=>!(t=>(t=t.replace(/\[/g,"\\[").replace(/\]/g,"\\]"),new RegExp("^("+t+")([>\\s~+[.,{:][\\s\\S]*)?$","m")))(e).test(t))(t,e)?((t,e,s)=>{const o="."+(e=e.replace(/\[is=([^\]]*)\]/g,(t,...e)=>e[0])),r=t=>{let r=t.trim();if(!r)return"";if(t.indexOf("-shadowcsshost-no-combinator")>-1)r=((t,e,s)=>{if(i.lastIndex=0,i.test(t)){const e="."+s;return t.replace(l,(t,s)=>S(s,e)).replace(i,e+" ")}return e+" "+t})(t,e,s);else{const e=t.replace(i,"");e.length>0&&(r=S(e,o))}return r},c=(t=>{const e=[];let s,o=0;return s=(t=t.replace(/(\[[^\]]*\])/g,(t,s)=>{const r=`__ph-${o}__`;return e.push(s),o++,r})).replace(/(:nth-[-\w]+)(\([^)]+\))/g,(t,s,r)=>{const c=`__ph-${o}__`;return e.push(r),o++,s+c}),{content:s,placeholders:e}})(t);let n,a="",h=0;const p=/( |>|\+|~(?!=))\s*/g;let d=!((t=c.content).indexOf("-shadowcsshost-no-combinator")>-1);for(;null!==(n=p.exec(t));){const e=n[1],s=t.slice(h,n.index).trim();d=d||s.indexOf("-shadowcsshost-no-combinator")>-1,a+=`${d?r(s):s} ${e} `,h=p.lastIndex}const u=t.substring(h);return d=d||u.indexOf("-shadowcsshost-no-combinator")>-1,a+=d?r(u):u,m=c.placeholders,a.replace(/__ph-(\d+)__/g,(t,e)=>m[+e]);var m})(t,e,s).trim():t.trim()).join(", "))(t.selector,e,s,o):(t.selector.startsWith("@media")||t.selector.startsWith("@supports")||t.selector.startsWith("@page")||t.selector.startsWith("@document"))&&(c=W(t.content,e,s,o)),{selector:r.replace(/\s{2,}/g," ").trim(),content:c}}),j=(t,e,s)=>{const o=e+"-h",l=e+"-s",i=t.match(m)||[];t=(t=>t.replace(u,""))(t);const g=[];if(s){const e=t=>{const e=`/*!@___${g.length}___*/`,s=`/*!@${t.selector}*/`;return g.push({placeholder:e,comment:s}),t.selector=e+t.selector,t};t=_(t,t=>"@"!==t.selector[0]?e(t):t.selector.startsWith("@media")||t.selector.startsWith("@supports")||t.selector.startsWith("@page")||t.selector.startsWith("@document")?(t.content=_(t.content,e),t):t)}const w=((t,e,s,o,l)=>{const i=((t,e)=>{const s="."+e+" > ",o=[];return t=t.replace(n,(...t)=>{if(t[2]){const e=t[2].trim(),r=t[3],c=s+e+r;let n="";for(let e=t[4]-1;e>=0;e--){const s=t[5][e];if("}"===s||","===s)break;n=s+n}const l=n+c,a=`${n.trimRight()}${c.trim()}`;if(l.trim()!==a.trim()){const t=`${a}, ${l}`;o.push({orgSelector:l,updatedSelector:t})}return c}return"-shadowcsshost-no-combinator"+t[3]}),{selectors:o,cssText:t}})(t=(t=>$(t,c,O))(t=(t=>$(t,r,b))(t=t.replace(d,"-shadowcsscontext").replace(h,"-shadowcsshost").replace(p,"-shadowcssslotted"))),o);return t=(t=>a.reduce((t,e)=>t.replace(e," "),t))(t=i.cssText),e&&(t=W(t,e,s,o)),{cssText:(t=(t=t.replace(/-shadowcsshost-no-combinator/g,"."+s)).replace(/>\s*\*\s+([^{, ]+)/gm," $1 ")).trim(),slottedSelectors:i.selectors}})(t,e,o,l);return t=[w.cssText,...i].join("\n"),s&&g.forEach(({placeholder:e,comment:s})=>{t=t.replace(e,s)}),w.slottedSelectors.forEach(e=>{t=t.replace(e.orgSelector,e.updatedSelector)}),t}}}]);
|
|
@ -1 +0,0 @@
|
|||
(window.webpackJsonp_font_awesome_admin=window.webpackJsonp_font_awesome_admin||[]).push([[18],{285:function(e,o,n){"use strict";n.r(o);var s=n(170),t=n.n(s);o.default=e=>t.a.get(e).then(e=>e.status>=200||e.satus<=299?e.data:(console.error(e),Promise.reject("Font Awesome plugin unexpected response for Icon Chooser"))).catch(e=>(console.error(e),Promise.reject(e)))}}]);
|
|
@ -1 +0,0 @@
|
|||
(window.webpackJsonp_font_awesome_admin=window.webpackJsonp_font_awesome_admin||[]).push([[19],{284:function(e,a,o){"use strict";o.r(a);var r=o(157),t=o.n(r);a.default=e=>async a=>{try{const{apiNonce:o,rootUrl:r,restApiNamespace:n}=e;return t.a.use(t.a.createRootURLMiddleware(r)),t.a.use(t.a.createNonceMiddleware(o)),await t()({path:n+"/api",method:"POST",body:a})}catch(e){throw console.error("CAUGHT:",e),new Error(e)}}}}]);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,10 @@
|
|||
.iVV55iNB320NJJLspy7m{max-width:600px}.BcmdF5mOoQ3Luug6sJrn{border:1px dotted grey;border-radius:5px;margin:1rem 1rem 1rem 0;padding:1rem}
|
||||
.lX8h3LbX6kaLN7_hLhlw{align-items:stretch;background-color:#e4f6ff;border-radius:.25rem;color:#495057;display:flex;flex-direction:row;flex-wrap:no-wrap;margin-top:1rem;max-width:800px}.nx2ZqeD9AnYnPnKHAqKJ{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem;color:#008bed;font-size:1rem;padding:.55rem .25rem .5rem .75rem}.ovRzytWn5jGccLKV78T9{font-size:.8rem;font-weight:600;line-height:1.5;margin-bottom:.2rem;margin-top:0}.M_C6Dj_EqhO8IuY52iA6{display:flex;flex-direction:column;flex-grow:1;font-size:.8rem;padding:.5rem 1rem .75rem .25rem}.M_C6Dj_EqhO8IuY52iA6 p{margin-bottom:.5rem;margin-top:0}.M_C6Dj_EqhO8IuY52iA6 svg{font-size:.7rem}.M_C6Dj_EqhO8IuY52iA6 ul{margin:0}.M_C6Dj_EqhO8IuY52iA6 li{display:inline-block;margin-bottom:0;padding-right:1rem}.liWjpcvKZkKaYPsJjQPA{margin-top:1rem}.lX8h3LbX6kaLN7_hLhlw button{color:#0073aa}.lX8h3LbX6kaLN7_hLhlw button:hover{color:#00a0d2}.MLwfZfK5uVZOtIHI1cdt{background:#fff9db}.MLwfZfK5uVZOtIHI1cdt .nx2ZqeD9AnYnPnKHAqKJ{color:#fab007}
|
||||
.uL1wb8HtJb_0IkG3PeEN{max-width:100%}.VJ5yoahvOPmye_Iv2x78 button{background:#0000;border:none;font-size:1.5em;margin-left:.125em;margin-right:.125em;padding-bottom:.25em}.VJ5yoahvOPmye_Iv2x78 button:hover{cursor:pointer}.VJ5yoahvOPmye_Iv2x78 button:disabled{border-bottom:4px solid #008ded;color:unset;cursor:default}
|
||||
.GfbntzmAC3JXYwdY57Hz{color:red}.nkCRdVrm4cTGk23nWmwZ{display:flex;width:100%}.nkCRdVrm4cTGk23nWmwZ svg{margin-bottom:.125em;margin-right:.5em}._V54L7D6KbeTsNGQHyuu{font-size:.9rem;font-weight:700;width:30%}.jH1jbDhGqqv7Grbj6p5p{margin-right:.25rem;text-align:center;width:1rem}.KYI6mFOoLc2LXE3_WOAV{display:flex;flex-direction:row}.uMUQXnIQhpFojiorojGR{width:50%}.Q3PqTXZI2xQbQuon9Brf{display:flex;flex-direction:column;margin-top:.5rem;width:70%}@media only screen and (min-width:1024px){.Q3PqTXZI2xQbQuon9Brf{margin-top:0}}.p0FxJ9gY4EvqdrEJ5pDs{font-style:italic}.p0FxJ9gY4EvqdrEJ5pDs>ul{list-style:none;margin-left:2em}.rr6y9ViQbhSy0YAp3Svc{display:flex;flex-direction:flex-row}.d104q4pTcfNp1fl3EEwj{font-weight:600;margin-left:5px}.pFNsNzsa6QtxNtsHMsos{margin-bottom:1rem;margin-top:1rem}.x21UupVYDcbhJEXG_vuB{margin-top:1rem}.x21UupVYDcbhJEXG_vuB button{background:none;border:none;cursor:pointer;text-decoration:underline}.x21UupVYDcbhJEXG_vuB button svg{margin-right:.5em}.elGAHNDAzrtnF0LZNYl_ .uMUQXnIQhpFojiorojGR{margin-bottom:1.5rem}.FHOPD8z6_efjfjjQHK9B{color:#868e96;display:block;font-weight:400;line-height:1.5;margin-top:.25rem}.FHOPD8z6_efjfjjQHK9B a{color:#868e96}.FHOPD8z6_efjfjjQHK9B a:hover{color:#228be6}.kP55KzmQ_7zoJ5baS76k{padding-left:1em}
|
||||
.xAYNgmh_FT28wOZEe4og{background-color:#fff;margin-right:20px;padding:1rem 2rem 2rem}@media only screen and (min-width:1024px){.xAYNgmh_FT28wOZEe4og{max-width:1000px}}.W3wz4Liah2EvWxtTBXN8{align-items:center;display:flex}.OpLLWfmNs6BXGmnmuinK{padding-left:1em}div.OpLLWfmNs6BXGmnmuinK.q0fCXPnTi1vRhNmP0IEt{background-color:#fda09a;border-radius:5px;display:flex;margin:1em;max-width:450px;padding:1em}div.OpLLWfmNs6BXGmnmuinK.q0fCXPnTi1vRhNmP0IEt .A2dLn8oZtVzuXngZMDRp{margin-bottom:auto;margin-top:auto}div.OpLLWfmNs6BXGmnmuinK.q0fCXPnTi1vRhNmP0IEt .xFoMk9Jc8Ir4n5Olcce1{max-width:400px}.OpLLWfmNs6BXGmnmuinK .xFoMk9Jc8Ir4n5Olcce1{padding-left:1em}.OpLLWfmNs6BXGmnmuinK.fQeEY3YNz4yh6R7vdi7J .JPBgwk6PxfiitLxJLE54{color:green}h2.VklefjWwawC59yrOPe3e{font-size:18px}h3.VklefjWwawC59yrOPe3e{font-size:16px}.e8Vu3y2YBkuW8N9IhY2m{margin-bottom:1rem;margin-top:1rem}.gNYVG50hxMZs8Gqbj_T0 th{font-weight:700}button.dpYyb_l0GWlAiVkOmmYt{background:none;border:0}.WJl_9YHKGkhUvtVwgVco{align-items:center;display:flex}.HBCEbIhIET1XISEYneSA{margin:1rem}button.ZXe2iyFqFThwx_UF4CBf{background:#008ded;border:solid #0064b1;border-radius:3px;border-width:1px 1px 4px;color:#fff;cursor:pointer;font-size:14px;font-weight:600;line-height:1.4em;padding:.7em 1.5em}button.ZXe2iyFqFThwx_UF4CBf[disabled]{background:#f8f9fa;border:1px solid #f8f9fa;color:#008ded;cursor:default}button .HgLyUkphZYd8YsLSMJAZ{display:inline-block;min-width:3.2em;text-align:left}.Gu2u4ZSZT25Yqm8zSogj{background-color:#fdfdf3;border:1px solid #000;border-radius:5px;max-width:600px;padding:1.5em}.WOV9bdVrpJVdQWzhBnHZ{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}@media only screen and (min-width:1024px){.QN_KH8sqi5QFBDqaH1rI{display:flex}}.bBMVcUUJf1GW7veG1Zic{flex-direction:row}.pIa2BGO1ABMtYZY185Bf{flex-direction:column}.h0koIl1zvME7toM3jUk9{position:relative}.kWqY7l_wn27HmdUNz7ZY .JPBgwk6PxfiitLxJLE54{font-size:24px}.kWqY7l_wn27HmdUNz7ZY.q3No9l7YMUEH1xvYTNfI .JPBgwk6PxfiitLxJLE54,.Y7M4JHzDp7jtCt6MonbK{color:green}.a6qTuZmDiKS_FHgMZawo,.kWqY7l_wn27HmdUNz7ZY.PwCQsIQEdGz9b0cOj3iA .JPBgwk6PxfiitLxJLE54{color:red}.kWqY7l_wn27HmdUNz7ZY.Gu2u4ZSZT25Yqm8zSogj .JPBgwk6PxfiitLxJLE54,.rw5FUVRrrdM17WyxcRZ9{color:#b7b700}.QEoklKhbCbwOUBs0cspa{margin-left:1rem}.oWHnpotXuoOIlJoqkkgw~label .NzRaF0U8aKPVtS6JIaK8,.gIUwcNcpOHhTKG4sTlfg~label .NzRaF0U8aKPVtS6JIaK8{display:none;opacity:0}.oWHnpotXuoOIlJoqkkgw:checked~label .NzRaF0U8aKPVtS6JIaK8,.gIUwcNcpOHhTKG4sTlfg:checked~label .NzRaF0U8aKPVtS6JIaK8{color:#228be6;display:block;opacity:1}.oWHnpotXuoOIlJoqkkgw:checked~label .iemYJRvB4tzF1xnuGiAw,.gIUwcNcpOHhTKG4sTlfg:checked~label .iemYJRvB4tzF1xnuGiAw{display:none;opacity:0}.oWHnpotXuoOIlJoqkkgw:checked~label .BFR5diS8tiViycbuTDVS,.oWHnpotXuoOIlJoqkkgw:checked~label .BFR5diS8tiViycbuTDVS a{color:#495057}.oWHnpotXuoOIlJoqkkgw:checked~label .BFR5diS8tiViycbuTDVS a:hover,.oWHnpotXuoOIlJoqkkgw~label .BFR5diS8tiViycbuTDVS a:hover{color:#228be6;text-decoration-color:initial}
|
||||
.kaJ7ZfPW8LYXB3jcG1xM{position:relative}.iw1ugzHiscI8cdqPxDt5{border-bottom:1px solid #dde2e6;display:flex;padding:1rem 1rem 1rem 0}.iw1ugzHiscI8cdqPxDt5 label{font-size:.9rem;font-weight:600;width:30%}.iw1ugzHiscI8cdqPxDt5 label svg{color:#dde2e6;padding-right:.5rem}.DnA1Iv_lwCTNSAFQGrON .iw1ugzHiscI8cdqPxDt5 p{font-size:unset;font-weight:unset}.DnA1Iv_lwCTNSAFQGrON .Pnf8O2SgfIFmVM0PSv3Z p svg{color:unset;padding-right:.5rem}.DnA1Iv_lwCTNSAFQGrON{border-bottom:1px solid #dde2e6;display:flex;flex-direction:column;margin-bottom:.75rem;padding:.5rem 1rem 1rem 0}.iw1ugzHiscI8cdqPxDt5.kjodIeFA7B16RQcru0GW{border-bottom:none}.Pnf8O2SgfIFmVM0PSv3Z{display:flex;justify-content:space-between}.Pnf8O2SgfIFmVM0PSv3Z.kjodIeFA7B16RQcru0GW{flex-direction:column}.DnA1Iv_lwCTNSAFQGrON p{font-size:.9rem;font-weight:600;margin:0;padding:.5rem 0}.Pnf8O2SgfIFmVM0PSv3Z .A0_oAmpyVJ9wdtADndGQ span svg{color:#00c346;padding-right:.5rem}button.HfzrDbHUd_u1i9ndGEBR{background-color:initial;border:none;border-radius:3px;color:#999;cursor:pointer;display:inline-block;margin-left:-.1em;padding:.5rem 1rem;transition:background .1s ease-in;transition:.1s ease-in}button.HfzrDbHUd_u1i9ndGEBR:hover{background-color:#da001d;color:#fff}.elgzg717O9Crp2uzkrTD button{margin-left:2em}.V9u2jF9aJPfN0wX4PDVS{background-color:none;border:none;cursor:pointer;line-height:2.15384615;margin-left:1rem;text-decoration:underline;text-decoration-color:#00000026}.V9u2jF9aJPfN0wX4PDVS:hover{text-decoration-color:#000}.wziOBkVmZ17vJu2Sz35G{border-bottom:1px solid #dde2e6;margin-bottom:.75rem;padding:.5rem 1rem 1rem 0}.DFhEV9q8j6_YiAqxGCIQ{font-size:.9rem;font-weight:600;margin:0;padding:.5rem 0}.DFhEV9q8j6_YiAqxGCIQ.y9VOhnGapgfPZMY_1Bh6 svg{color:#00c346;padding-right:.5rem}.DFhEV9q8j6_YiAqxGCIQ.czVzoPvuSXhcttrzFc9L svg{color:#f8f9fa}.ngog_nfdj6MCATJVipdj{padding:.5rem 1rem 1rem 0}.S_T55Hlv5ASeRm2CyHmV{font-size:.9rem;font-weight:600;margin:0 0 1rem}.S_T55Hlv5ASeRm2CyHmV svg{color:#dde2e6;padding-right:.5rem}.fy5GsBzkO8Epk2RR04LH{margin-left:1.8rem}button.fNJ_UaCGqOZxk72kjIDA{background-color:initial;border:none;border-radius:3px;color:#228be6;cursor:pointer;display:inline-block;margin:0 0 0 .2rem;padding:.5rem;transition:background .1s ease-in;transition:.1s ease-in;vertical-align:middle}button.fNJ_UaCGqOZxk72kjIDA:hover{background-color:#1c7ed6;color:#fff}button.fNJ_UaCGqOZxk72kjIDA span{padding-left:.5em}.SstVIjmK5UOaiy_ltjXE .vdGJ8TWieYuqqCakSyHG{display:inline-block;font-weight:600;height:auto;margin-right:1rem;padding:.4rem;vertical-align:middle;width:30%}.gJq7WtlHzhBcjrgjABY8{margin-left:1.8rem}.o1IZC2E8wgWEslXswxvX{border-collapse:collapse;font-size:.9rem;margin:0 0 1rem;width:100%}.o1IZC2E8wgWEslXswxvX .W3xNRcvtnX8v6hHiakrQ,.o1IZC2E8wgWEslXswxvX .HS7POK_5ddxGDSTzJHun{border-bottom:1px solid #dde2e6;border-top:1px solid #dde2e6;padding:.5rem;text-align:left;vertical-align:top}.o1IZC2E8wgWEslXswxvX .W3xNRcvtnX8v6hHiakrQ{font-weight:600;width:30%}.co95Sqvd5n7VgX7SM5jt{display:block;font-weight:400;line-height:1.5;margin-top:.25rem}.co95Sqvd5n7VgX7SM5jt,.co95Sqvd5n7VgX7SM5jt a{color:#868e96}.co95Sqvd5n7VgX7SM5jt a:hover{color:#228be6}
|
||||
.wVJC_TuxmtpxI03Tbdkt{border-bottom:2px solid #008ded;display:flex;margin-bottom:.5rem;padding-bottom:1rem}.wVJC_TuxmtpxI03Tbdkt label{margin-right:1rem}
|
||||
|
||||
.FGrSfvJewATz8TfOqA_j th.dDmxKRAWr1lhLPK3Z838,td.dDmxKRAWr1lhLPK3Z838{background-color:#ffe2e2}
|
||||
.heZgRJQYY60l5e4s0W_4 th{vertical-align:top}.heZgRJQYY60l5e4s0W_4 th .cBkIuJWm4fbhOOHopdph{font-weight:700}.heZgRJQYY60l5e4s0W_4 code{font-size:10px}.qxjS23M34RH041PZzC82,.L1uULhjJTYD39y7vA6HC{margin-top:.5rem}.JL6BMdxHE5CPnMDBfHe8{display:flex}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,10 @@
|
|||
.iVV55iNB320NJJLspy7m{max-width:600px}.BcmdF5mOoQ3Luug6sJrn{border:1px dotted grey;border-radius:5px;margin:1rem 0 1rem 1rem;padding:1rem}
|
||||
.lX8h3LbX6kaLN7_hLhlw{align-items:stretch;background-color:#e4f6ff;border-radius:.25rem;color:#495057;display:flex;flex-direction:row;flex-wrap:no-wrap;margin-top:1rem;max-width:800px}.nx2ZqeD9AnYnPnKHAqKJ{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem;color:#008bed;font-size:1rem;padding:.55rem .75rem .5rem .25rem}.ovRzytWn5jGccLKV78T9{font-size:.8rem;font-weight:600;line-height:1.5;margin-bottom:.2rem;margin-top:0}.M_C6Dj_EqhO8IuY52iA6{display:flex;flex-direction:column;flex-grow:1;font-size:.8rem;padding:.5rem .25rem .75rem 1rem}.M_C6Dj_EqhO8IuY52iA6 p{margin-bottom:.5rem;margin-top:0}.M_C6Dj_EqhO8IuY52iA6 svg{font-size:.7rem}.M_C6Dj_EqhO8IuY52iA6 ul{margin:0}.M_C6Dj_EqhO8IuY52iA6 li{display:inline-block;margin-bottom:0;padding-left:1rem}.liWjpcvKZkKaYPsJjQPA{margin-top:1rem}.lX8h3LbX6kaLN7_hLhlw button{color:#0073aa}.lX8h3LbX6kaLN7_hLhlw button:hover{color:#00a0d2}.MLwfZfK5uVZOtIHI1cdt{background:#fff9db}.MLwfZfK5uVZOtIHI1cdt .nx2ZqeD9AnYnPnKHAqKJ{color:#fab007}
|
||||
.uL1wb8HtJb_0IkG3PeEN{max-width:100%}.VJ5yoahvOPmye_Iv2x78 button{background:#0000;border:none;font-size:1.5em;margin-right:.125em;margin-left:.125em;padding-bottom:.25em}.VJ5yoahvOPmye_Iv2x78 button:hover{cursor:pointer}.VJ5yoahvOPmye_Iv2x78 button:disabled{border-bottom:4px solid #008ded;color:unset;cursor:default}
|
||||
.GfbntzmAC3JXYwdY57Hz{color:red}.nkCRdVrm4cTGk23nWmwZ{display:flex;width:100%}.nkCRdVrm4cTGk23nWmwZ svg{margin-bottom:.125em;margin-left:.5em}._V54L7D6KbeTsNGQHyuu{font-size:.9rem;font-weight:700;width:30%}.jH1jbDhGqqv7Grbj6p5p{margin-left:.25rem;text-align:center;width:1rem}.KYI6mFOoLc2LXE3_WOAV{display:flex;flex-direction:row}.uMUQXnIQhpFojiorojGR{width:50%}.Q3PqTXZI2xQbQuon9Brf{display:flex;flex-direction:column;margin-top:.5rem;width:70%}@media only screen and (min-width:1024px){.Q3PqTXZI2xQbQuon9Brf{margin-top:0}}.p0FxJ9gY4EvqdrEJ5pDs{font-style:italic}.p0FxJ9gY4EvqdrEJ5pDs>ul{list-style:none;margin-right:2em}.rr6y9ViQbhSy0YAp3Svc{display:flex;flex-direction:flex-row}.d104q4pTcfNp1fl3EEwj{font-weight:600;margin-right:5px}.pFNsNzsa6QtxNtsHMsos{margin-bottom:1rem;margin-top:1rem}.x21UupVYDcbhJEXG_vuB{margin-top:1rem}.x21UupVYDcbhJEXG_vuB button{background:none;border:none;cursor:pointer;text-decoration:underline}.x21UupVYDcbhJEXG_vuB button svg{margin-left:.5em}.elGAHNDAzrtnF0LZNYl_ .uMUQXnIQhpFojiorojGR{margin-bottom:1.5rem}.FHOPD8z6_efjfjjQHK9B{color:#868e96;display:block;font-weight:400;line-height:1.5;margin-top:.25rem}.FHOPD8z6_efjfjjQHK9B a{color:#868e96}.FHOPD8z6_efjfjjQHK9B a:hover{color:#228be6}.kP55KzmQ_7zoJ5baS76k{padding-right:1em}
|
||||
.xAYNgmh_FT28wOZEe4og{background-color:#fff;margin-left:20px;padding:1rem 2rem 2rem}@media only screen and (min-width:1024px){.xAYNgmh_FT28wOZEe4og{max-width:1000px}}.W3wz4Liah2EvWxtTBXN8{align-items:center;display:flex}.OpLLWfmNs6BXGmnmuinK{padding-right:1em}div.OpLLWfmNs6BXGmnmuinK.q0fCXPnTi1vRhNmP0IEt{background-color:#fda09a;border-radius:5px;display:flex;margin:1em;max-width:450px;padding:1em}div.OpLLWfmNs6BXGmnmuinK.q0fCXPnTi1vRhNmP0IEt .A2dLn8oZtVzuXngZMDRp{margin-bottom:auto;margin-top:auto}div.OpLLWfmNs6BXGmnmuinK.q0fCXPnTi1vRhNmP0IEt .xFoMk9Jc8Ir4n5Olcce1{max-width:400px}.OpLLWfmNs6BXGmnmuinK .xFoMk9Jc8Ir4n5Olcce1{padding-right:1em}.OpLLWfmNs6BXGmnmuinK.fQeEY3YNz4yh6R7vdi7J .JPBgwk6PxfiitLxJLE54{color:green}h2.VklefjWwawC59yrOPe3e{font-size:18px}h3.VklefjWwawC59yrOPe3e{font-size:16px}.e8Vu3y2YBkuW8N9IhY2m{margin-bottom:1rem;margin-top:1rem}.gNYVG50hxMZs8Gqbj_T0 th{font-weight:700}button.dpYyb_l0GWlAiVkOmmYt{background:none;border:0}.WJl_9YHKGkhUvtVwgVco{align-items:center;display:flex}.HBCEbIhIET1XISEYneSA{margin:1rem}button.ZXe2iyFqFThwx_UF4CBf{background:#008ded;border:solid #0064b1;border-radius:3px;border-width:1px 1px 4px;color:#fff;cursor:pointer;font-size:14px;font-weight:600;line-height:1.4em;padding:.7em 1.5em}button.ZXe2iyFqFThwx_UF4CBf[disabled]{background:#f8f9fa;border:1px solid #f8f9fa;color:#008ded;cursor:default}button .HgLyUkphZYd8YsLSMJAZ{display:inline-block;min-width:3.2em;text-align:right}.Gu2u4ZSZT25Yqm8zSogj{background-color:#fdfdf3;border:1px solid #000;border-radius:5px;max-width:600px;padding:1.5em}.WOV9bdVrpJVdQWzhBnHZ{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}@media only screen and (min-width:1024px){.QN_KH8sqi5QFBDqaH1rI{display:flex}}.bBMVcUUJf1GW7veG1Zic{flex-direction:row}.pIa2BGO1ABMtYZY185Bf{flex-direction:column}.h0koIl1zvME7toM3jUk9{position:relative}.kWqY7l_wn27HmdUNz7ZY .JPBgwk6PxfiitLxJLE54{font-size:24px}.kWqY7l_wn27HmdUNz7ZY.q3No9l7YMUEH1xvYTNfI .JPBgwk6PxfiitLxJLE54,.Y7M4JHzDp7jtCt6MonbK{color:green}.a6qTuZmDiKS_FHgMZawo,.kWqY7l_wn27HmdUNz7ZY.PwCQsIQEdGz9b0cOj3iA .JPBgwk6PxfiitLxJLE54{color:red}.kWqY7l_wn27HmdUNz7ZY.Gu2u4ZSZT25Yqm8zSogj .JPBgwk6PxfiitLxJLE54,.rw5FUVRrrdM17WyxcRZ9{color:#b7b700}.QEoklKhbCbwOUBs0cspa{margin-right:1rem}.oWHnpotXuoOIlJoqkkgw~label .NzRaF0U8aKPVtS6JIaK8,.gIUwcNcpOHhTKG4sTlfg~label .NzRaF0U8aKPVtS6JIaK8{display:none;opacity:0}.oWHnpotXuoOIlJoqkkgw:checked~label .NzRaF0U8aKPVtS6JIaK8,.gIUwcNcpOHhTKG4sTlfg:checked~label .NzRaF0U8aKPVtS6JIaK8{color:#228be6;display:block;opacity:1}.oWHnpotXuoOIlJoqkkgw:checked~label .iemYJRvB4tzF1xnuGiAw,.gIUwcNcpOHhTKG4sTlfg:checked~label .iemYJRvB4tzF1xnuGiAw{display:none;opacity:0}.oWHnpotXuoOIlJoqkkgw:checked~label .BFR5diS8tiViycbuTDVS,.oWHnpotXuoOIlJoqkkgw:checked~label .BFR5diS8tiViycbuTDVS a{color:#495057}.oWHnpotXuoOIlJoqkkgw:checked~label .BFR5diS8tiViycbuTDVS a:hover,.oWHnpotXuoOIlJoqkkgw~label .BFR5diS8tiViycbuTDVS a:hover{color:#228be6;text-decoration-color:initial}
|
||||
.kaJ7ZfPW8LYXB3jcG1xM{position:relative}.iw1ugzHiscI8cdqPxDt5{border-bottom:1px solid #dde2e6;display:flex;padding:1rem 0 1rem 1rem}.iw1ugzHiscI8cdqPxDt5 label{font-size:.9rem;font-weight:600;width:30%}.iw1ugzHiscI8cdqPxDt5 label svg{color:#dde2e6;padding-left:.5rem}.DnA1Iv_lwCTNSAFQGrON .iw1ugzHiscI8cdqPxDt5 p{font-size:unset;font-weight:unset}.DnA1Iv_lwCTNSAFQGrON .Pnf8O2SgfIFmVM0PSv3Z p svg{color:unset;padding-left:.5rem}.DnA1Iv_lwCTNSAFQGrON{border-bottom:1px solid #dde2e6;display:flex;flex-direction:column;margin-bottom:.75rem;padding:.5rem 0 1rem 1rem}.iw1ugzHiscI8cdqPxDt5.kjodIeFA7B16RQcru0GW{border-bottom:none}.Pnf8O2SgfIFmVM0PSv3Z{display:flex;justify-content:space-between}.Pnf8O2SgfIFmVM0PSv3Z.kjodIeFA7B16RQcru0GW{flex-direction:column}.DnA1Iv_lwCTNSAFQGrON p{font-size:.9rem;font-weight:600;margin:0;padding:.5rem 0}.Pnf8O2SgfIFmVM0PSv3Z .A0_oAmpyVJ9wdtADndGQ span svg{color:#00c346;padding-left:.5rem}button.HfzrDbHUd_u1i9ndGEBR{background-color:initial;border:none;border-radius:3px;color:#999;cursor:pointer;display:inline-block;margin-right:-.1em;padding:.5rem 1rem;transition:background .1s ease-in;transition:.1s ease-in}button.HfzrDbHUd_u1i9ndGEBR:hover{background-color:#da001d;color:#fff}.elgzg717O9Crp2uzkrTD button{margin-right:2em}.V9u2jF9aJPfN0wX4PDVS{background-color:none;border:none;cursor:pointer;line-height:2.15384615;margin-right:1rem;text-decoration:underline;text-decoration-color:#00000026}.V9u2jF9aJPfN0wX4PDVS:hover{text-decoration-color:#000}.wziOBkVmZ17vJu2Sz35G{border-bottom:1px solid #dde2e6;margin-bottom:.75rem;padding:.5rem 0 1rem 1rem}.DFhEV9q8j6_YiAqxGCIQ{font-size:.9rem;font-weight:600;margin:0;padding:.5rem 0}.DFhEV9q8j6_YiAqxGCIQ.y9VOhnGapgfPZMY_1Bh6 svg{color:#00c346;padding-left:.5rem}.DFhEV9q8j6_YiAqxGCIQ.czVzoPvuSXhcttrzFc9L svg{color:#f8f9fa}.ngog_nfdj6MCATJVipdj{padding:.5rem 0 1rem 1rem}.S_T55Hlv5ASeRm2CyHmV{font-size:.9rem;font-weight:600;margin:0 0 1rem}.S_T55Hlv5ASeRm2CyHmV svg{color:#dde2e6;padding-left:.5rem}.fy5GsBzkO8Epk2RR04LH{margin-right:1.8rem}button.fNJ_UaCGqOZxk72kjIDA{background-color:initial;border:none;border-radius:3px;color:#228be6;cursor:pointer;display:inline-block;margin:0 .2rem 0 0;padding:.5rem;transition:background .1s ease-in;transition:.1s ease-in;vertical-align:middle}button.fNJ_UaCGqOZxk72kjIDA:hover{background-color:#1c7ed6;color:#fff}button.fNJ_UaCGqOZxk72kjIDA span{padding-right:.5em}.SstVIjmK5UOaiy_ltjXE .vdGJ8TWieYuqqCakSyHG{display:inline-block;font-weight:600;height:auto;margin-left:1rem;padding:.4rem;vertical-align:middle;width:30%}.gJq7WtlHzhBcjrgjABY8{margin-right:1.8rem}.o1IZC2E8wgWEslXswxvX{border-collapse:collapse;font-size:.9rem;margin:0 0 1rem;width:100%}.o1IZC2E8wgWEslXswxvX .W3xNRcvtnX8v6hHiakrQ,.o1IZC2E8wgWEslXswxvX .HS7POK_5ddxGDSTzJHun{border-bottom:1px solid #dde2e6;border-top:1px solid #dde2e6;padding:.5rem;text-align:right;vertical-align:top}.o1IZC2E8wgWEslXswxvX .W3xNRcvtnX8v6hHiakrQ{font-weight:600;width:30%}.co95Sqvd5n7VgX7SM5jt{display:block;font-weight:400;line-height:1.5;margin-top:.25rem}.co95Sqvd5n7VgX7SM5jt,.co95Sqvd5n7VgX7SM5jt a{color:#868e96}.co95Sqvd5n7VgX7SM5jt a:hover{color:#228be6}
|
||||
.wVJC_TuxmtpxI03Tbdkt{border-bottom:2px solid #008ded;display:flex;margin-bottom:.5rem;padding-bottom:1rem}.wVJC_TuxmtpxI03Tbdkt label{margin-left:1rem}
|
||||
|
||||
.FGrSfvJewATz8TfOqA_j th.dDmxKRAWr1lhLPK3Z838,td.dDmxKRAWr1lhLPK3Z838{background-color:#ffe2e2}
|
||||
.heZgRJQYY60l5e4s0W_4 th{vertical-align:top}.heZgRJQYY60l5e4s0W_4 th .cBkIuJWm4fbhOOHopdph{font-weight:700}.heZgRJQYY60l5e4s0W_4 code{font-size:10px}.qxjS23M34RH041PZzC82,.L1uULhjJTYD39y7vA6HC{margin-top:.5rem}.JL6BMdxHE5CPnMDBfHe8{display:flex}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,3 @@
|
|||
.xAYNgmh_FT28wOZEe4og{background-color:#fff;margin-right:20px;padding:1rem 2rem 2rem}@media only screen and (min-width:1024px){.xAYNgmh_FT28wOZEe4og{max-width:1000px}}.W3wz4Liah2EvWxtTBXN8{align-items:center;display:flex}.OpLLWfmNs6BXGmnmuinK{padding-left:1em}div.OpLLWfmNs6BXGmnmuinK.q0fCXPnTi1vRhNmP0IEt{background-color:#fda09a;border-radius:5px;display:flex;margin:1em;max-width:450px;padding:1em}div.OpLLWfmNs6BXGmnmuinK.q0fCXPnTi1vRhNmP0IEt .A2dLn8oZtVzuXngZMDRp{margin-bottom:auto;margin-top:auto}div.OpLLWfmNs6BXGmnmuinK.q0fCXPnTi1vRhNmP0IEt .xFoMk9Jc8Ir4n5Olcce1{max-width:400px}.OpLLWfmNs6BXGmnmuinK .xFoMk9Jc8Ir4n5Olcce1{padding-left:1em}.OpLLWfmNs6BXGmnmuinK.fQeEY3YNz4yh6R7vdi7J .JPBgwk6PxfiitLxJLE54{color:green}h2.VklefjWwawC59yrOPe3e{font-size:18px}h3.VklefjWwawC59yrOPe3e{font-size:16px}.e8Vu3y2YBkuW8N9IhY2m{margin-bottom:1rem;margin-top:1rem}.gNYVG50hxMZs8Gqbj_T0 th{font-weight:700}button.dpYyb_l0GWlAiVkOmmYt{background:none;border:0}.WJl_9YHKGkhUvtVwgVco{align-items:center;display:flex}.HBCEbIhIET1XISEYneSA{margin:1rem}button.ZXe2iyFqFThwx_UF4CBf{background:#008ded;border:solid #0064b1;border-radius:3px;border-width:1px 1px 4px;color:#fff;cursor:pointer;font-size:14px;font-weight:600;line-height:1.4em;padding:.7em 1.5em}button.ZXe2iyFqFThwx_UF4CBf[disabled]{background:#f8f9fa;border:1px solid #f8f9fa;color:#008ded;cursor:default}button .HgLyUkphZYd8YsLSMJAZ{display:inline-block;min-width:3.2em;text-align:left}.Gu2u4ZSZT25Yqm8zSogj{background-color:#fdfdf3;border:1px solid #000;border-radius:5px;max-width:600px;padding:1.5em}.WOV9bdVrpJVdQWzhBnHZ{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}@media only screen and (min-width:1024px){.QN_KH8sqi5QFBDqaH1rI{display:flex}}.bBMVcUUJf1GW7veG1Zic{flex-direction:row}.pIa2BGO1ABMtYZY185Bf{flex-direction:column}.h0koIl1zvME7toM3jUk9{position:relative}.kWqY7l_wn27HmdUNz7ZY .JPBgwk6PxfiitLxJLE54{font-size:24px}.kWqY7l_wn27HmdUNz7ZY.q3No9l7YMUEH1xvYTNfI .JPBgwk6PxfiitLxJLE54,.Y7M4JHzDp7jtCt6MonbK{color:green}.a6qTuZmDiKS_FHgMZawo,.kWqY7l_wn27HmdUNz7ZY.PwCQsIQEdGz9b0cOj3iA .JPBgwk6PxfiitLxJLE54{color:red}.kWqY7l_wn27HmdUNz7ZY.Gu2u4ZSZT25Yqm8zSogj .JPBgwk6PxfiitLxJLE54,.rw5FUVRrrdM17WyxcRZ9{color:#b7b700}.QEoklKhbCbwOUBs0cspa{margin-left:1rem}.oWHnpotXuoOIlJoqkkgw~label .NzRaF0U8aKPVtS6JIaK8,.gIUwcNcpOHhTKG4sTlfg~label .NzRaF0U8aKPVtS6JIaK8{display:none;opacity:0}.oWHnpotXuoOIlJoqkkgw:checked~label .NzRaF0U8aKPVtS6JIaK8,.gIUwcNcpOHhTKG4sTlfg:checked~label .NzRaF0U8aKPVtS6JIaK8{color:#228be6;display:block;opacity:1}.oWHnpotXuoOIlJoqkkgw:checked~label .iemYJRvB4tzF1xnuGiAw,.gIUwcNcpOHhTKG4sTlfg:checked~label .iemYJRvB4tzF1xnuGiAw{display:none;opacity:0}.oWHnpotXuoOIlJoqkkgw:checked~label .BFR5diS8tiViycbuTDVS,.oWHnpotXuoOIlJoqkkgw:checked~label .BFR5diS8tiViycbuTDVS a{color:#495057}.oWHnpotXuoOIlJoqkkgw:checked~label .BFR5diS8tiViycbuTDVS a:hover,.oWHnpotXuoOIlJoqkkgw~label .BFR5diS8tiViycbuTDVS a:hover{color:#228be6;text-decoration-color:initial}
|
||||
.iVV55iNB320NJJLspy7m{max-width:600px}.BcmdF5mOoQ3Luug6sJrn{border:1px dotted grey;border-radius:5px;margin:1rem 1rem 1rem 0;padding:1rem}
|
||||
.lX8h3LbX6kaLN7_hLhlw{align-items:stretch;background-color:#e4f6ff;border-radius:.25rem;color:#495057;display:flex;flex-direction:row;flex-wrap:no-wrap;margin-top:1rem;max-width:800px}.nx2ZqeD9AnYnPnKHAqKJ{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem;color:#008bed;font-size:1rem;padding:.55rem .25rem .5rem .75rem}.ovRzytWn5jGccLKV78T9{font-size:.8rem;font-weight:600;line-height:1.5;margin-bottom:.2rem;margin-top:0}.M_C6Dj_EqhO8IuY52iA6{display:flex;flex-direction:column;flex-grow:1;font-size:.8rem;padding:.5rem 1rem .75rem .25rem}.M_C6Dj_EqhO8IuY52iA6 p{margin-bottom:.5rem;margin-top:0}.M_C6Dj_EqhO8IuY52iA6 svg{font-size:.7rem}.M_C6Dj_EqhO8IuY52iA6 ul{margin:0}.M_C6Dj_EqhO8IuY52iA6 li{display:inline-block;margin-bottom:0;padding-right:1rem}.liWjpcvKZkKaYPsJjQPA{margin-top:1rem}.lX8h3LbX6kaLN7_hLhlw button{color:#0073aa}.lX8h3LbX6kaLN7_hLhlw button:hover{color:#00a0d2}.MLwfZfK5uVZOtIHI1cdt{background:#fff9db}.MLwfZfK5uVZOtIHI1cdt .nx2ZqeD9AnYnPnKHAqKJ{color:#fab007}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,3 @@
|
|||
.xAYNgmh_FT28wOZEe4og{background-color:#fff;margin-left:20px;padding:1rem 2rem 2rem}@media only screen and (min-width:1024px){.xAYNgmh_FT28wOZEe4og{max-width:1000px}}.W3wz4Liah2EvWxtTBXN8{align-items:center;display:flex}.OpLLWfmNs6BXGmnmuinK{padding-right:1em}div.OpLLWfmNs6BXGmnmuinK.q0fCXPnTi1vRhNmP0IEt{background-color:#fda09a;border-radius:5px;display:flex;margin:1em;max-width:450px;padding:1em}div.OpLLWfmNs6BXGmnmuinK.q0fCXPnTi1vRhNmP0IEt .A2dLn8oZtVzuXngZMDRp{margin-bottom:auto;margin-top:auto}div.OpLLWfmNs6BXGmnmuinK.q0fCXPnTi1vRhNmP0IEt .xFoMk9Jc8Ir4n5Olcce1{max-width:400px}.OpLLWfmNs6BXGmnmuinK .xFoMk9Jc8Ir4n5Olcce1{padding-right:1em}.OpLLWfmNs6BXGmnmuinK.fQeEY3YNz4yh6R7vdi7J .JPBgwk6PxfiitLxJLE54{color:green}h2.VklefjWwawC59yrOPe3e{font-size:18px}h3.VklefjWwawC59yrOPe3e{font-size:16px}.e8Vu3y2YBkuW8N9IhY2m{margin-bottom:1rem;margin-top:1rem}.gNYVG50hxMZs8Gqbj_T0 th{font-weight:700}button.dpYyb_l0GWlAiVkOmmYt{background:none;border:0}.WJl_9YHKGkhUvtVwgVco{align-items:center;display:flex}.HBCEbIhIET1XISEYneSA{margin:1rem}button.ZXe2iyFqFThwx_UF4CBf{background:#008ded;border:solid #0064b1;border-radius:3px;border-width:1px 1px 4px;color:#fff;cursor:pointer;font-size:14px;font-weight:600;line-height:1.4em;padding:.7em 1.5em}button.ZXe2iyFqFThwx_UF4CBf[disabled]{background:#f8f9fa;border:1px solid #f8f9fa;color:#008ded;cursor:default}button .HgLyUkphZYd8YsLSMJAZ{display:inline-block;min-width:3.2em;text-align:right}.Gu2u4ZSZT25Yqm8zSogj{background-color:#fdfdf3;border:1px solid #000;border-radius:5px;max-width:600px;padding:1.5em}.WOV9bdVrpJVdQWzhBnHZ{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}@media only screen and (min-width:1024px){.QN_KH8sqi5QFBDqaH1rI{display:flex}}.bBMVcUUJf1GW7veG1Zic{flex-direction:row}.pIa2BGO1ABMtYZY185Bf{flex-direction:column}.h0koIl1zvME7toM3jUk9{position:relative}.kWqY7l_wn27HmdUNz7ZY .JPBgwk6PxfiitLxJLE54{font-size:24px}.kWqY7l_wn27HmdUNz7ZY.q3No9l7YMUEH1xvYTNfI .JPBgwk6PxfiitLxJLE54,.Y7M4JHzDp7jtCt6MonbK{color:green}.a6qTuZmDiKS_FHgMZawo,.kWqY7l_wn27HmdUNz7ZY.PwCQsIQEdGz9b0cOj3iA .JPBgwk6PxfiitLxJLE54{color:red}.kWqY7l_wn27HmdUNz7ZY.Gu2u4ZSZT25Yqm8zSogj .JPBgwk6PxfiitLxJLE54,.rw5FUVRrrdM17WyxcRZ9{color:#b7b700}.QEoklKhbCbwOUBs0cspa{margin-right:1rem}.oWHnpotXuoOIlJoqkkgw~label .NzRaF0U8aKPVtS6JIaK8,.gIUwcNcpOHhTKG4sTlfg~label .NzRaF0U8aKPVtS6JIaK8{display:none;opacity:0}.oWHnpotXuoOIlJoqkkgw:checked~label .NzRaF0U8aKPVtS6JIaK8,.gIUwcNcpOHhTKG4sTlfg:checked~label .NzRaF0U8aKPVtS6JIaK8{color:#228be6;display:block;opacity:1}.oWHnpotXuoOIlJoqkkgw:checked~label .iemYJRvB4tzF1xnuGiAw,.gIUwcNcpOHhTKG4sTlfg:checked~label .iemYJRvB4tzF1xnuGiAw{display:none;opacity:0}.oWHnpotXuoOIlJoqkkgw:checked~label .BFR5diS8tiViycbuTDVS,.oWHnpotXuoOIlJoqkkgw:checked~label .BFR5diS8tiViycbuTDVS a{color:#495057}.oWHnpotXuoOIlJoqkkgw:checked~label .BFR5diS8tiViycbuTDVS a:hover,.oWHnpotXuoOIlJoqkkgw~label .BFR5diS8tiViycbuTDVS a:hover{color:#228be6;text-decoration-color:initial}
|
||||
.iVV55iNB320NJJLspy7m{max-width:600px}.BcmdF5mOoQ3Luug6sJrn{border:1px dotted grey;border-radius:5px;margin:1rem 0 1rem 1rem;padding:1rem}
|
||||
.lX8h3LbX6kaLN7_hLhlw{align-items:stretch;background-color:#e4f6ff;border-radius:.25rem;color:#495057;display:flex;flex-direction:row;flex-wrap:no-wrap;margin-top:1rem;max-width:800px}.nx2ZqeD9AnYnPnKHAqKJ{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem;color:#008bed;font-size:1rem;padding:.55rem .75rem .5rem .25rem}.ovRzytWn5jGccLKV78T9{font-size:.8rem;font-weight:600;line-height:1.5;margin-bottom:.2rem;margin-top:0}.M_C6Dj_EqhO8IuY52iA6{display:flex;flex-direction:column;flex-grow:1;font-size:.8rem;padding:.5rem .25rem .75rem 1rem}.M_C6Dj_EqhO8IuY52iA6 p{margin-bottom:.5rem;margin-top:0}.M_C6Dj_EqhO8IuY52iA6 svg{font-size:.7rem}.M_C6Dj_EqhO8IuY52iA6 ul{margin:0}.M_C6Dj_EqhO8IuY52iA6 li{display:inline-block;margin-bottom:0;padding-left:1rem}.liWjpcvKZkKaYPsJjQPA{margin-top:1rem}.lX8h3LbX6kaLN7_hLhlw button{color:#0073aa}.lX8h3LbX6kaLN7_hLhlw button:hover{color:#00a0d2}.MLwfZfK5uVZOtIHI1cdt{background:#fff9db}.MLwfZfK5uVZOtIHI1cdt .nx2ZqeD9AnYnPnKHAqKJ{color:#fab007}
|
|
@ -1,701 +0,0 @@
|
|||
.d7wuKQTkcJufIbd\+gVhKnw\=\= {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.F6JwARlyXPrf\+kIygaN8dQ\=\= {
|
||||
padding: 1rem;
|
||||
margin: 1rem 1rem 1rem 0rem;
|
||||
border: 1px dotted grey;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.v2APGCcZUAaU68TnPHhvxw\=\= {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: no-wrap;
|
||||
align-items: stretch;
|
||||
background-color: #E4F6FF;
|
||||
color: rgb(73, 80, 87);
|
||||
border-radius: 0.25rem;
|
||||
margin-top: 1rem;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.syPwBWS1kp-zUKz4hcgcXg\=\= {
|
||||
color: #008BED;
|
||||
border-top-left-radius: 0.25rem;
|
||||
border-bottom-left-radius: 0.25rem;
|
||||
padding: .55rem .25rem .5rem .75rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.ptjLX6BwJtUff-P6OkZBiA\=\= {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.2rem;
|
||||
font-size: .8rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.VAB708TLB4qhUVdnQGAxJA\=\= {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: .5rem 1rem .75rem .25rem;
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
.VAB708TLB4qhUVdnQGAxJA\=\= p {
|
||||
margin-top: 0;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
.VAB708TLB4qhUVdnQGAxJA\=\= svg {
|
||||
font-size: .7rem;
|
||||
}
|
||||
|
||||
.VAB708TLB4qhUVdnQGAxJA\=\= ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.VAB708TLB4qhUVdnQGAxJA\=\= li {
|
||||
display: inline-block;
|
||||
padding-right: 1rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.CIIJrcA\+PLxU-W4xIVozXw\=\= {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.v2APGCcZUAaU68TnPHhvxw\=\= button {
|
||||
color: #0073aa;
|
||||
}
|
||||
|
||||
.v2APGCcZUAaU68TnPHhvxw\=\= button:hover {
|
||||
color: #00a0d2;
|
||||
}
|
||||
|
||||
/* type: warning */
|
||||
.iAbTOYj3VuCpNr1NEwmL4g\=\= {
|
||||
background: rgb(255, 249, 219);
|
||||
}
|
||||
|
||||
.iAbTOYj3VuCpNr1NEwmL4g\=\= .syPwBWS1kp-zUKz4hcgcXg\=\= {
|
||||
color: rgb(250, 176, 7);
|
||||
}
|
||||
|
||||
|
||||
.QIGY8oMzb821jqSAA6Rn4g\=\= {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.aALIlyz\+TBWS9MLVeh6wmA\=\= button {
|
||||
font-size: 1.5em;
|
||||
background: transparent;
|
||||
margin-right: 0.125em;
|
||||
margin-left: 0.125em;
|
||||
border: none;
|
||||
padding-bottom: 0.25em;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.aALIlyz\+TBWS9MLVeh6wmA\=\= button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.aALIlyz\+TBWS9MLVeh6wmA\=\= button:disabled {
|
||||
cursor: default;
|
||||
border-bottom: 4px solid #008DED;
|
||||
color: unset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
._0x0RK8KiAJgDczKK031HxQ\=\= {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.hyAooutsnRTCptVmz96wbg\=\= {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hyAooutsnRTCptVmz96wbg\=\= svg {
|
||||
margin-right: .5em;
|
||||
margin-bottom: .125em;
|
||||
}
|
||||
|
||||
.BawvAG3StpFE9UbNiPvK3g\=\= {
|
||||
width: 30%;
|
||||
font-weight: bold;
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
.w9F7a0jctEk6w8ugAV5ABA\=\= {
|
||||
width: 1rem;
|
||||
margin-right: .25rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.BRC\+YZNrGUxfH82gZsrdwA\=\= {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.tuTbOAR46RTbuDXH00d2gQ\=\= {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.BnGZOlJKEKc\+YI9KmQh\+Yg\=\= {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 70%;
|
||||
margin-top: .5rem;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1024px) {
|
||||
.BnGZOlJKEKc\+YI9KmQh\+Yg\=\= {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.iCLEbIAXAx8VH8RGCCbvgg\=\= {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.iCLEbIAXAx8VH8RGCCbvgg\=\= > ul {
|
||||
list-style: none;
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
.QZrz215uMWhNgkyzgTmOag\=\= {
|
||||
display: flex;
|
||||
flex-direction: flex-row;
|
||||
}
|
||||
|
||||
.zjk9n76WnC7PKioF3aBPNA\=\= {
|
||||
margin-left: 5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.JgyF2w3zr8u8EZtHirZ9jw\=\= {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.l90FgLRSjN56djI2bGkw0A\=\= {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.l90FgLRSjN56djI2bGkw0A\=\= button {
|
||||
background: none;
|
||||
border: none;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.l90FgLRSjN56djI2bGkw0A\=\= button svg {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.nszEb3XbyS\+sLm8WSjoWSw\=\= .tuTbOAR46RTbuDXH00d2gQ\=\= {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.m4T9E7AFyJOV9pxI900Q3Q\=\= {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
line-height: 1.5;
|
||||
margin-top: .25rem;
|
||||
color: #868e96;
|
||||
}
|
||||
|
||||
.m4T9E7AFyJOV9pxI900Q3Q\=\= a {
|
||||
color: #868e96;
|
||||
}
|
||||
|
||||
.m4T9E7AFyJOV9pxI900Q3Q\=\= a:hover {
|
||||
color: #228be6;
|
||||
}
|
||||
|
||||
.-HAQ9msG6UCBe4TdGvJ5Gw\=\= {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.Ihb3kyONjVgEWV0zB0vfxQ\=\= {
|
||||
margin-right: 20px;
|
||||
padding: 1rem 2rem 2rem 2rem;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1024px) {
|
||||
.Ihb3kyONjVgEWV0zB0vfxQ\=\= {
|
||||
max-width: 1000px;
|
||||
}
|
||||
}
|
||||
|
||||
.ToMOGjAmxSPs6D\+glWGx9A\=\= {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
._3edKMOIt9iftk0Zz-7ia9w\=\= {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
div._3edKMOIt9iftk0Zz-7ia9w\=\=.NbqVQtCU5W3ihwasrYexfA\=\= {
|
||||
display: flex;
|
||||
margin: 1em;
|
||||
background-color: #fda09a;
|
||||
border-radius: 5px;
|
||||
max-width: 450px;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
div._3edKMOIt9iftk0Zz-7ia9w\=\=.NbqVQtCU5W3ihwasrYexfA\=\= .gEl1fl\+2lk74ueiQz5cPxQ\=\= {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
div._3edKMOIt9iftk0Zz-7ia9w\=\=.NbqVQtCU5W3ihwasrYexfA\=\= .vumGDcuTrv0Ekcc\+McKiXw\=\= {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
._3edKMOIt9iftk0Zz-7ia9w\=\= .vumGDcuTrv0Ekcc\+McKiXw\=\= {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
._3edKMOIt9iftk0Zz-7ia9w\=\=._7IGgJmOfwN1O\+c0smUgA9Q\=\= .b1NRGX9AXkY1BfJ1MCfPTw\=\= {
|
||||
color: green;
|
||||
}
|
||||
|
||||
h2._3hBTOhCdvQjibQScAbzMIQ\=\= {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h3._3hBTOhCdvQjibQScAbzMIQ\=\= {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
._0XZF4B-SzNg4vm8Dl0F7TA\=\= {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.YGhcFGkqFAeqpY9iNYq9Sw\=\= th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button.mQ\+dGgo7ePYMdFVM7UJy3Q\=\= {
|
||||
border: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.LZqubosol4XlcTmVPXrgwA\=\= {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
._92G6m9T1MVtvbrtbN0ztew\=\= {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
button.vKD-ffoQma2PtYJ6syJLXA\=\= {
|
||||
border: 1px solid #0064B1;
|
||||
border-bottom: 4px solid #0064B1;
|
||||
border-radius: 3px;
|
||||
padding: .7em 1.5em;
|
||||
background: #008DED;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 1.4em;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button.vKD-ffoQma2PtYJ6syJLXA\=\=[disabled] {
|
||||
border: 1px solid #F8F9FA;
|
||||
background: #F8F9FA;
|
||||
color: #008DED;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
button .jRDHFr0fk9vh0tmPg3yyNA\=\= {
|
||||
display: inline-block;
|
||||
min-width: 3.2em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.BHff-dIr\+7jxh1slKud1UA\=\= {
|
||||
background-color: #fdfdf3;
|
||||
max-width: 600px;
|
||||
padding: 1.5em;
|
||||
border-radius: 5px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
._8sv48aq5xq1UY1HM-IXXWw\=\= {
|
||||
border: 0;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1024px) {
|
||||
.XWyrhxEjrFCimjviedIRKg\=\= {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.KIG-iO8JlK18PTTkxmFFfQ\=\= {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.wbV6cqcB6HeXauGMWUOUJw\=\= {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.PWf16KXgVsL5DasX-69r\+w\=\= {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.BFnd\+XqC\+F5AvKCZ6eMOOA\=\= .b1NRGX9AXkY1BfJ1MCfPTw\=\= {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.BFnd\+XqC\+F5AvKCZ6eMOOA\=\=.x86R9\+0TG6mMWrDQqDWMxQ\=\= .b1NRGX9AXkY1BfJ1MCfPTw\=\=, ._8fwuockVscy-LVkmd5sRrg\=\= {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.BFnd\+XqC\+F5AvKCZ6eMOOA\=\=._4ywMQ6iToIUtlBzG0klUZQ\=\= .b1NRGX9AXkY1BfJ1MCfPTw\=\=, .n2ieOzL8DYqXeYvR4dnlBQ\=\= {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.BFnd\+XqC\+F5AvKCZ6eMOOA\=\=.BHff-dIr\+7jxh1slKud1UA\=\= .b1NRGX9AXkY1BfJ1MCfPTw\=\=, .Kc0JjWetOt7bzIP5T5F-3g\=\= {
|
||||
color: #b7b700;
|
||||
}
|
||||
|
||||
.cdItXesO30xESmpowZCWVA\=\= {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.XrgwDjPx-AobEiR9810sug\=\= ~ label .wfGA8rTfLXMNYeed\+7P0mg\=\=, .YGOg\+3jg-Q6uUjZrsKJJBw\=\= ~ label .wfGA8rTfLXMNYeed\+7P0mg\=\= {
|
||||
display:none;
|
||||
opacity:0;
|
||||
}
|
||||
|
||||
.XrgwDjPx-AobEiR9810sug\=\=:checked ~ label .wfGA8rTfLXMNYeed\+7P0mg\=\=, .YGOg\+3jg-Q6uUjZrsKJJBw\=\=:checked ~ label .wfGA8rTfLXMNYeed\+7P0mg\=\={
|
||||
display:block;
|
||||
opacity:1.0;
|
||||
color: #228be6;
|
||||
}
|
||||
|
||||
.XrgwDjPx-AobEiR9810sug\=\=:checked ~ label .dxKTDsQBZGG-O07iRE7TNg\=\=, .YGOg\+3jg-Q6uUjZrsKJJBw\=\=:checked ~ label .dxKTDsQBZGG-O07iRE7TNg\=\={
|
||||
display:none;
|
||||
opacity:0;
|
||||
}
|
||||
|
||||
.YGOg\+3jg-Q6uUjZrsKJJBw\=\=:checked ~ label .AX07i\+p1n9K\+g4HYk3mvOg\=\= {
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.YGOg\+3jg-Q6uUjZrsKJJBw\=\=:checked ~ label .AX07i\+p1n9K\+g4HYk3mvOg\=\= a {
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.YGOg\+3jg-Q6uUjZrsKJJBw\=\=:checked ~ label .AX07i\+p1n9K\+g4HYk3mvOg\=\= a:hover,
|
||||
.YGOg\+3jg-Q6uUjZrsKJJBw\=\= ~ label .AX07i\+p1n9K\+g4HYk3mvOg\=\= a:hover {
|
||||
color: #228be6;
|
||||
-webkit-text-decoration-color: initial;
|
||||
text-decoration-color: initial;
|
||||
}
|
||||
|
||||
|
||||
/* Kit tab */
|
||||
|
||||
._8vGwfwQ6XO5BsPMbXdXTiA\=\= {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* API token */
|
||||
|
||||
.\+8WVAaXCYDQCF\+JpeqOS\+w\=\= {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #DDE2E6;
|
||||
padding: 1rem 1rem 1rem 0;
|
||||
}
|
||||
|
||||
.\+8WVAaXCYDQCF\+JpeqOS\+w\=\= label {
|
||||
width: 30%;
|
||||
font-size: .9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.\+8WVAaXCYDQCF\+JpeqOS\+w\=\= label svg {
|
||||
padding-right: .5rem;
|
||||
color: #DDE2E6;
|
||||
}
|
||||
|
||||
.NNaEQMl0NwcAO9BQkOMVjA\=\= .\+8WVAaXCYDQCF\+JpeqOS\+w\=\= p {
|
||||
font-size: unset;
|
||||
font-weight: unset;
|
||||
}
|
||||
|
||||
.NNaEQMl0NwcAO9BQkOMVjA\=\= .Uk7ZhRxwlPWH-ZTew\+MGbw\=\= p svg {
|
||||
padding-right: .5rem;
|
||||
color: unset;
|
||||
}
|
||||
|
||||
.NNaEQMl0NwcAO9BQkOMVjA\=\= {
|
||||
display: flex;
|
||||
margin-bottom: .75rem;
|
||||
padding: .5rem 1rem 1rem 0;
|
||||
border-bottom: 1px solid #DDE2E6;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.\+8WVAaXCYDQCF\+JpeqOS\+w\=\=.EwijJfw66yu\+IIrz6lhCfg\=\= {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.Uk7ZhRxwlPWH-ZTew\+MGbw\=\= {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.Uk7ZhRxwlPWH-ZTew\+MGbw\=\=.EwijJfw66yu\+IIrz6lhCfg\=\= {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.NNaEQMl0NwcAO9BQkOMVjA\=\= p {
|
||||
margin: 0;
|
||||
padding: .5rem 0;
|
||||
font-size: .9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.Uk7ZhRxwlPWH-ZTew\+MGbw\=\= .EGcS\+kpw3Rpch27QNPDt8A\=\= span svg {
|
||||
padding-right: .5rem;
|
||||
color: #00C346;
|
||||
}
|
||||
|
||||
button.IMhiWaWNyRtqLVii4y5GUw\=\= {
|
||||
transition: background .1s ease-in;
|
||||
transition: 0.1s ease-in;
|
||||
display: inline-block;
|
||||
margin-left: -.1em;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
background-color: transparent;
|
||||
padding: .5rem 1rem;
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button.IMhiWaWNyRtqLVii4y5GUw\=\=:hover {
|
||||
background-color: #DA001D;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.oIc2sd4DIsbfgvBY8l-wPw\=\= button {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
.gxUD1N\+WoBmH-EJlqniWkw\=\= {
|
||||
margin-left: 1rem;
|
||||
line-height: 2.15384615;
|
||||
border: none;
|
||||
background-color: none;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration-color: rgba(0, 0, 0, 0.15);
|
||||
text-decoration-color: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.gxUD1N\+WoBmH-EJlqniWkw\=\=:hover {
|
||||
-webkit-text-decoration-color: black;
|
||||
text-decoration-color: black;
|
||||
}
|
||||
|
||||
/* Active kit info */
|
||||
.zPZer73GvIkmxZ7KreNJYA\=\= {
|
||||
margin-bottom: .75rem;
|
||||
border-bottom: 1px solid #DDE2E6;
|
||||
padding: .5rem 1rem 1rem 0;
|
||||
}
|
||||
|
||||
.J-fVpwkR2DjY\+-8RSoJYMw\=\= {
|
||||
margin: 0;
|
||||
padding: .5rem 0;
|
||||
font-size: .9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.J-fVpwkR2DjY\+-8RSoJYMw\=\=.lycO-c64O\+OgDI9YkMivUw\=\= svg {
|
||||
padding-right: .5rem;
|
||||
color: #00C346;
|
||||
}
|
||||
|
||||
.J-fVpwkR2DjY\+-8RSoJYMw\=\=.szbUIrRpgoLKxf9N4EyKLg\=\= svg {
|
||||
color: #F8F9FA;
|
||||
}
|
||||
|
||||
|
||||
/* Select/View kit data */
|
||||
|
||||
.-pzVBDa-b25g\+W8geRXnxg\=\= {
|
||||
padding: .5rem 1rem 1rem 0;
|
||||
}
|
||||
|
||||
.-FLmUWzoR6pLlIRSuFBBYA\=\= {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: .9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.-FLmUWzoR6pLlIRSuFBBYA\=\= svg {
|
||||
padding-right: .5rem;
|
||||
color: #DDE2E6;
|
||||
}
|
||||
|
||||
._4hIkSVrBxe2EsX5xI4X\+-w\=\= {
|
||||
margin-left: 1.8rem;
|
||||
}
|
||||
|
||||
button.PGpv2OOpBHnM9TF49ScwhQ\=\= {
|
||||
transition: background .1s ease-in;
|
||||
transition: 0.1s ease-in;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin: 0 0 0 .2rem;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
background-color: transparent;
|
||||
padding: .5rem;
|
||||
color: #228be6;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button.PGpv2OOpBHnM9TF49ScwhQ\=\=:hover {
|
||||
background-color: #1c7ed6;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button.PGpv2OOpBHnM9TF49ScwhQ\=\= span {
|
||||
padding-left: .5em;
|
||||
}
|
||||
|
||||
.TCMt1wxwCSzZQpcKueWmkQ\=\= .D-w2AnOwJY2bw5sGz5McjA\=\= {
|
||||
display: inline-block;
|
||||
width: 30%;
|
||||
height: auto;
|
||||
vertical-align: middle;
|
||||
margin-right: 1rem;
|
||||
padding: .4rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Kit settings table */
|
||||
|
||||
.oJVokNJ-sIuA\+gdUpTLGJA\=\= {
|
||||
margin-left: 1.8rem;
|
||||
}
|
||||
|
||||
.EczpWfVlPxzdEaHRV3pz2A\=\= {
|
||||
width: 100%;
|
||||
margin: 0 0 1rem 0;
|
||||
border-collapse: collapse;
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
.EczpWfVlPxzdEaHRV3pz2A\=\= .-qghjrb3DvBDmVsGriWvTA\=\=,
|
||||
.EczpWfVlPxzdEaHRV3pz2A\=\= .YObcB6LSN4LrZ85qknFF-w\=\= {
|
||||
padding: .5rem;
|
||||
text-align: left;
|
||||
border-top: 1px solid #DDE2E6;
|
||||
border-bottom: 1px solid #DDE2E6;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.EczpWfVlPxzdEaHRV3pz2A\=\= .-qghjrb3DvBDmVsGriWvTA\=\= {
|
||||
font-weight: 600;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
._9FAtxV9kie\+OmOmzBfJSkA\=\= {
|
||||
display: block;
|
||||
margin-top: .25rem;
|
||||
font-weight: normal;
|
||||
line-height: 1.5;
|
||||
color: #868e96;
|
||||
}
|
||||
|
||||
._9FAtxV9kie\+OmOmzBfJSkA\=\= a {
|
||||
color: #868e96;
|
||||
}
|
||||
|
||||
._9FAtxV9kie\+OmOmzBfJSkA\=\= a:hover {
|
||||
color: #228be6;
|
||||
}
|
||||
|
||||
.c\+uCz1PFg7ovVDH7oUd5Sw\=\= {
|
||||
display: flex;
|
||||
margin-bottom: .5rem;
|
||||
border-bottom: 2px solid #008DED;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.c\+uCz1PFg7ovVDH7oUd5Sw\=\= label {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
|
||||
.gRCffi6JWAwOkiNdtjZueA\=\= th.agGiNQAP3Ikmo1HKogxbiA\=\=, td.agGiNQAP3Ikmo1HKogxbiA\=\= {
|
||||
background-color: #FFE2E2;
|
||||
}
|
||||
|
||||
.B\+Bikng94RbtcQqPqBrHOA\=\= th {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.B\+Bikng94RbtcQqPqBrHOA\=\= th .iRlCLvShkhGZ\+9zO5xq6uw\=\= {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.B\+Bikng94RbtcQqPqBrHOA\=\= code {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
._9-S9B47DLrx4-SEnbiDx1Q\=\=, .b72L8oHK2yOpCV0to2lb7Q\=\= {
|
||||
margin-top: .5rem;
|
||||
}
|
||||
|
||||
.va5cD9uCSEXRsb4FfvMXgQ\=\= {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.VkiLBdIWC85apP6l5wk2TQ\=\= {
|
||||
border: 1px solid black;
|
||||
background-color: #fdfdf3;
|
||||
padding: 1.5em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.GaudPtB1hhO0HHjMCEqy4Q\=\= {
|
||||
padding: .5rem;
|
||||
background-color: rgba(0,0,0,0);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.GaudPtB1hhO0HHjMCEqy4Q\=\=:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.GaudPtB1hhO0HHjMCEqy4Q\=\= .sp3bMuQjz4\+THc5BC0vcRg\=\= {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
"use strict";(self.webpackChunkfont_awesome_admin=self.webpackChunkfont_awesome_admin||[]).push([[897],{897:(c,e,a)=>{a.d(e,{GEE:()=>f,Nfw:()=>l,SGM:()=>i,wRm:()=>n});const i={prefix:"far",iconName:"circle-check",icon:[512,512,[61533,"check-circle"],"f058","M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0l-111 111-47-47c-9.4-9.4-24.6-9.4-33.9 0s-9.4 24.6 0 33.9l64 64c9.4 9.4 24.6 9.4 33.9 0L369 209z"]},l={prefix:"far",iconName:"square",icon:[448,512,[9632,9723,9724,61590],"f0c8","M384 80c8.8 0 16 7.2 16 16l0 320c0 8.8-7.2 16-16 16L64 432c-8.8 0-16-7.2-16-16L48 96c0-8.8 7.2-16 16-16l320 0zM64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-320c0-35.3-28.7-64-64-64L64 32z"]},f={prefix:"far",iconName:"circle",icon:[512,512,[128308,128309,128992,128993,128994,128995,128996,9679,9898,9899,11044,61708,61915],"f111","M464 256A208 208 0 1 0 48 256a208 208 0 1 0 416 0zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256z"]},n={prefix:"far",iconName:"circle-question",icon:[512,512,[62108,"question-circle"],"f059","M464 256A208 208 0 1 0 48 256a208 208 0 1 0 416 0zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zm169.8-90.7c7.9-22.3 29.1-37.3 52.8-37.3l58.3 0c34.9 0 63.1 28.3 63.1 63.1c0 22.6-12.1 43.5-31.7 54.8L280 264.4c-.2 13-10.9 23.6-24 23.6c-13.3 0-24-10.7-24-24l0-13.5c0-8.6 4.6-16.5 12.1-20.8l44.3-25.4c4.7-2.7 7.6-7.7 7.6-13.1c0-8.4-6.8-15.1-15.1-15.1l-58.3 0c-3.4 0-6.4 2.1-7.5 5.3l-.4 1.2c-4.4 12.5-18.2 19-30.6 14.6s-19-18.2-14.6-30.6l.4-1.2zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"]}}}]);
|
|
@ -1 +0,0 @@
|
|||
(window.webpackJsonp_font_awesome_admin=window.webpackJsonp_font_awesome_admin||[]).push([[9],{174:function(n,r,e){"use strict";e.d(r,"a",(function(){return t})),e.d(r,"d",(function(){return i})),e.d(r,"b",(function(){return o})),e.d(r,"c",(function(){return c}));var t={prefix:"far",iconName:"circle-check",icon:[512,512,[61533,"check-circle"],"f058","M243.8 339.8C232.9 350.7 215.1 350.7 204.2 339.8L140.2 275.8C129.3 264.9 129.3 247.1 140.2 236.2C151.1 225.3 168.9 225.3 179.8 236.2L224 280.4L332.2 172.2C343.1 161.3 360.9 161.3 371.8 172.2C382.7 183.1 382.7 200.9 371.8 211.8L243.8 339.8zM512 256C512 397.4 397.4 512 256 512C114.6 512 0 397.4 0 256C0 114.6 114.6 0 256 0C397.4 0 512 114.6 512 256zM256 48C141.1 48 48 141.1 48 256C48 370.9 141.1 464 256 464C370.9 464 464 370.9 464 256C464 141.1 370.9 48 256 48z"]},i={prefix:"far",iconName:"square",icon:[448,512,[9632,9723,9724,61590],"f0c8","M384 32C419.3 32 448 60.65 448 96V416C448 451.3 419.3 480 384 480H64C28.65 480 0 451.3 0 416V96C0 60.65 28.65 32 64 32H384zM384 80H64C55.16 80 48 87.16 48 96V416C48 424.8 55.16 432 64 432H384C392.8 432 400 424.8 400 416V96C400 87.16 392.8 80 384 80z"]},o={prefix:"far",iconName:"circle",icon:[512,512,[128308,128309,128992,128993,128994,128995,128996,9679,9898,9899,11044,61708,61915],"f111","M512 256C512 397.4 397.4 512 256 512C114.6 512 0 397.4 0 256C0 114.6 114.6 0 256 0C397.4 0 512 114.6 512 256zM256 48C141.1 48 48 141.1 48 256C48 370.9 141.1 464 256 464C370.9 464 464 370.9 464 256C464 141.1 370.9 48 256 48z"]},c={prefix:"far",iconName:"circle-question",icon:[512,512,[62108,"question-circle"],"f059","M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 464c-114.7 0-208-93.31-208-208S141.3 48 256 48s208 93.31 208 208S370.7 464 256 464zM256 336c-18 0-32 14-32 32s13.1 32 32 32c17.1 0 32-14 32-32S273.1 336 256 336zM289.1 128h-51.1C199 128 168 159 168 198c0 13 11 24 24 24s24-11 24-24C216 186 225.1 176 237.1 176h51.1C301.1 176 312 186 312 198c0 8-4 14.1-11 18.1L244 251C236 256 232 264 232 272V288c0 13 11 24 24 24S280 301 280 288V286l45.1-28c21-13 34-36 34-60C360 159 329 128 289.1 128z"]}},258:function(n,r,e){var t=e(60),i=e(201),o=e(54),c=e(7),f=e(259),u=e(53),a=e(202),s=e(183),l=e(59),C=/\w*$/;n.exports=function(n,r){var e=30,v="...";if(c(r)){var p="separator"in r?r.separator:p;e="length"in r?s(r.length):e,v="omission"in r?t(r.omission):v}var x=(n=l(n)).length;if(o(n)){var h=a(n);x=h.length}if(e>=x)return n;var g=e-u(v);if(g<1)return v;var d=h?i(h,0,g).join(""):n.slice(0,g);if(void 0===p)return d+v;if(h&&(g+=d.length-g),f(p)){if(n.slice(g).search(p)){var z,M=d;for(p.global||(p=RegExp(p.source,l(C.exec(p))+"g")),p.lastIndex=0;z=p.exec(M);)var m=z.index;d=d.slice(0,void 0===m?g:m)}}else if(n.indexOf(t(p),g)!=g){var w=d.lastIndexOf(p);w>-1&&(d=d.slice(0,w))}return d+v}},259:function(n,r,e){var t=e(260),i=e(17),o=e(19),c=o&&o.isRegExp,f=c?i(c):t;n.exports=f},260:function(n,r,e){var t=e(4),i=e(3);n.exports=function(n){return i(n)&&"[object RegExp]"==t(n)}},263:function(n,r,e){var t=e(182);n.exports=function(n,r){return t(n,r)}},264:function(n,r,e){var t=e(265);n.exports=function(n){return n&&n.length?t(n):[]}},265:function(n,r,e){var t=e(33);n.exports=function(n,r){for(var e=-1,i=n.length,o=0,c=[];++e<i;){var f=n[e],u=r?r(f):f;if(!e||!t(u,a)){var a=u;c[o++]=0===f?0:f}}return c}},266:function(n,r,e){var t=e(267),i=e(67),o=e(273),c=e(274),f=o((function(n,r){return c(n)?t(n,i(r,1,c,!0)):[]}));n.exports=f},267:function(n,r,e){var t=e(195),i=e(268),o=e(272),c=e(32),f=e(17),u=e(196);n.exports=function(n,r,e,a){var s=-1,l=i,C=!0,v=n.length,p=[],x=r.length;if(!v)return p;e&&(r=c(r,f(e))),a?(l=o,C=!1):r.length>=200&&(l=u,C=!1,r=new t(r));n:for(;++s<v;){var h=n[s],g=null==e?h:e(h);if(h=a||0!==h?h:0,C&&g==g){for(var d=x;d--;)if(r[d]===g)continue n;p.push(h)}else l(r,g,a)||p.push(h)}return p}},268:function(n,r,e){var t=e(269);n.exports=function(n,r){return!(null==n||!n.length)&&t(n,r,0)>-1}},269:function(n,r,e){var t=e(200),i=e(270),o=e(271);n.exports=function(n,r,e){return r==r?o(n,r,e):t(n,i,e)}},270:function(n,r){n.exports=function(n){return n!=n}},271:function(n,r){n.exports=function(n,r,e){for(var t=e-1,i=n.length;++t<i;)if(n[t]===r)return t;return-1}},272:function(n,r){n.exports=function(n,r,e){for(var t=-1,i=null==n?0:n.length;++t<i;)if(e(r,n[t]))return!0;return!1}},273:function(n,r,e){var t=e(62),i=e(68),o=e(69);n.exports=function(n,r){return o(i(n,r,t),n+"")}},274:function(n,r,e){var t=e(16),i=e(3);n.exports=function(n){return i(n)&&t(n)}}}]);
|
|
@ -0,0 +1 @@
|
|||
<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-dom-ready', 'wp-element', 'wp-i18n'), 'version' => 'c533036ab18a17f3f9bc');
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -5,4 +5,4 @@ process.env.WP_BASE_URL = `http://${process.env.WP_DOMAIN}`
|
|||
process.env.WP_USERNAME = process.env.WP_ADMIN_USERNAME
|
||||
process.env.WP_PASSWORD = process.env.WP_ADMIN_PASSWORD
|
||||
|
||||
module.exports = defaultConfig;
|
||||
module.exports = defaultConfig
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,27 +1,24 @@
|
|||
{
|
||||
"name": "font-awesome-admin",
|
||||
"version": "4.3.1",
|
||||
"version": "5.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fa-icon-chooser-react": "0.4.1",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.2.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.2.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.0",
|
||||
"@fortawesome/react-fontawesome": "^0.1.3",
|
||||
"@wordpress/components": "14.1.10",
|
||||
"axios": "^0.25.0",
|
||||
"classnames": "^2.2.6",
|
||||
"react-redux": "^7.1.1",
|
||||
"react-shadow-dom-retarget-events": "^1.0.11",
|
||||
"redux": "^4.0.4",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"web-vitals": "^1.0.1",
|
||||
"use-subscription": "1.5.1",
|
||||
"moment": "^2.29.4",
|
||||
"moment-timezone": "^0.5.35"
|
||||
"axios": "^1.7.4",
|
||||
"classnames": "^2.5.1",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"react-redux": "^8",
|
||||
"react-shadow-dom-retarget-events": "^1.1.0",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"web-vitals": "^4.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "wp-scripts build --webpack-no-externals",
|
||||
"build": "wp-scripts build",
|
||||
"check-engines": "wp-scripts check-engines",
|
||||
"check-licenses": "wp-scripts check-licenses",
|
||||
"format": "wp-scripts format",
|
||||
|
@ -31,8 +28,9 @@
|
|||
"lint:md:js": "wp-scripts lint-md-js",
|
||||
"lint:pkg-json": "wp-scripts lint-pkg-json",
|
||||
"packages-update": "wp-scripts packages-update",
|
||||
"start": "wp-scripts start --webpack-no-externals",
|
||||
"start": "wp-scripts start",
|
||||
"test:e2e": "wp-scripts test-e2e",
|
||||
"test:playwright": "npx playwright test",
|
||||
"test:unit": "wp-scripts test-unit-js",
|
||||
"test": "npm run test:unit"
|
||||
},
|
||||
|
@ -55,28 +53,27 @@
|
|||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@wordpress/babel-preset-default": "6.2.0",
|
||||
"@wordpress/components": "14.1.10",
|
||||
"@wordpress/compose": "4.1.5",
|
||||
"@wordpress/data": "5.1.5",
|
||||
"@wordpress/e2e-test-utils": "^5.4.10",
|
||||
"@wordpress/element": "3.1.1",
|
||||
"@wordpress/i18n": "^3.16.0",
|
||||
"@wordpress/icons": "4.0.2",
|
||||
"@wordpress/jest-preset-default": "7.0.5",
|
||||
"@wordpress/primitives": "2.1.1",
|
||||
"@wordpress/rich-text": "4.1.5",
|
||||
"@wordpress/scripts": "16.1.4",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"dotenv": "^14.2.0",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-adapter-react-16": "^1.15.0",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-test-renderer": "16.13.1",
|
||||
"@wordpress/babel-preset-default": "wp-6.7",
|
||||
"@wordpress/block-editor": "wp-6.7",
|
||||
"@wordpress/blocks": "wp-6.7",
|
||||
"@wordpress/components": "wp-6.7",
|
||||
"@wordpress/compose": "wp-6.7",
|
||||
"@wordpress/data": "wp-6.7",
|
||||
"@wordpress/e2e-test-utils": "wp-6.7",
|
||||
"@wordpress/e2e-test-utils-playwright": "^0.26.0",
|
||||
"@wordpress/i18n": "wp-6.7",
|
||||
"@wordpress/icons": "wp-6.7",
|
||||
"@wordpress/jest-preset-default": "wp-6.7",
|
||||
"@wordpress/keyboard-shortcuts": "wp-6.7",
|
||||
"@wordpress/scripts": "wp-6.7",
|
||||
"decode-uri-component": "^0.4.1",
|
||||
"dotenv": "^14.3.2",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mysql2": "^3.9.9",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"redux-mock-store": "^1.5.4",
|
||||
"rewire": "^5.0.0",
|
||||
"terser": "4.8.1",
|
||||
"webpack-bundle-analyzer": "^4.4.2"
|
||||
"rewire": "^7.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import './src/playwright/support/env.js'
|
||||
import { defineConfig, devices } from '@playwright/test'
|
||||
|
||||
const testDir = 'src/playwright'
|
||||
const baseURL = `http://${process.env.WP_DOMAIN}`
|
||||
process.env.WP_BASE_URL = baseURL
|
||||
const adminStorageStatePath = 'src/playwright/.auth/state.json'
|
||||
|
||||
export default defineConfig({
|
||||
use: {
|
||||
baseURL
|
||||
},
|
||||
projects: [
|
||||
{ name: 'auth', testDir, testMatch: 'setup/auth.js' },
|
||||
{
|
||||
name: 'reset',
|
||||
testDir,
|
||||
testMatch: 'setup/reset.js',
|
||||
use: {
|
||||
storageState: adminStorageStatePath
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'setupProKit',
|
||||
testDir,
|
||||
testMatch: 'setup/proKit.js',
|
||||
use: {
|
||||
storageState: adminStorageStatePath
|
||||
},
|
||||
dependencies: ['auth', 'reset']
|
||||
},
|
||||
{
|
||||
name: 'with-proKit-chromium',
|
||||
testMatch: 'withProKit/*.spec.js',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: adminStorageStatePath
|
||||
},
|
||||
dependencies: ['setupProKit']
|
||||
},
|
||||
{
|
||||
name: 'withAuth-chromium',
|
||||
testMatch: 'withAuth/*.spec.js',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: adminStorageStatePath
|
||||
},
|
||||
dependencies: ['auth', 'reset']
|
||||
}
|
||||
]
|
||||
})
|
|
@ -3,51 +3,73 @@ import PropTypes from 'prop-types'
|
|||
import styles from './Alert.module.css'
|
||||
import classnames from 'classnames'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import {
|
||||
faInfoCircle,
|
||||
faThumbsUp,
|
||||
faSpinner,
|
||||
faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faInfoCircle, faThumbsUp, faSpinner, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
function getIcon(props = {}) {
|
||||
switch (props.type) {
|
||||
case 'info':
|
||||
return <FontAwesomeIcon icon={ faInfoCircle } title='info' fixedWidth />
|
||||
return (
|
||||
<FontAwesomeIcon
|
||||
icon={faInfoCircle}
|
||||
title="info"
|
||||
fixedWidth
|
||||
/>
|
||||
)
|
||||
case 'warning':
|
||||
return <FontAwesomeIcon icon={ faExclamationTriangle } title='warning' fixedWidth />
|
||||
return (
|
||||
<FontAwesomeIcon
|
||||
icon={faExclamationTriangle}
|
||||
title="warning"
|
||||
fixedWidth
|
||||
/>
|
||||
)
|
||||
case 'pending':
|
||||
return <FontAwesomeIcon icon={ faSpinner } title='pending' spin fixedWidth />
|
||||
return (
|
||||
<FontAwesomeIcon
|
||||
icon={faSpinner}
|
||||
title="pending"
|
||||
spin
|
||||
fixedWidth
|
||||
/>
|
||||
)
|
||||
case 'success':
|
||||
return <FontAwesomeIcon icon={ faThumbsUp } title='success' fixedWidth />
|
||||
return (
|
||||
<FontAwesomeIcon
|
||||
icon={faThumbsUp}
|
||||
title="success"
|
||||
fixedWidth
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return <FontAwesomeIcon icon={ faExclamationTriangle } title='warning' fixedWidth />
|
||||
return (
|
||||
<FontAwesomeIcon
|
||||
icon={faExclamationTriangle}
|
||||
title="warning"
|
||||
fixedWidth
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function Alert(props = {}) {
|
||||
return <div className={ classnames(styles['alert'], styles[`alert-${ props.type }`]) } role="alert">
|
||||
<div className={ styles['alert-icon'] }>
|
||||
{ getIcon(props) }
|
||||
</div>
|
||||
return (
|
||||
<div
|
||||
className={classnames(styles['alert'], styles[`alert-${props.type}`])}
|
||||
role="alert"
|
||||
>
|
||||
<div className={styles['alert-icon']}>{getIcon(props)}</div>
|
||||
<div className={styles['alert-message']}>
|
||||
<h2 className={ styles['alert-title'] }>
|
||||
{ props.title }
|
||||
</h2>
|
||||
<div className={ styles['alert-copy'] }>
|
||||
{ props.children }
|
||||
</div>
|
||||
<h2 className={styles['alert-title']}>{props.title}</h2>
|
||||
<div className={styles['alert-copy']}>{props.children}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Alert.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
type: PropTypes.oneOf(['info', 'warning', 'success', 'pending']),
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.object,
|
||||
PropTypes.string,
|
||||
PropTypes.arrayOf(PropTypes.element)
|
||||
]).isRequired
|
||||
children: PropTypes.oneOfType([PropTypes.object, PropTypes.string, PropTypes.arrayOf(PropTypes.element)]).isRequired
|
||||
}
|
||||
|
||||
export default Alert
|
||||
|
|
|
@ -1,23 +1,15 @@
|
|||
import React from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import {
|
||||
addPendingOption,
|
||||
checkPreferenceConflicts
|
||||
} from './store/actions'
|
||||
import { addPendingOption, checkPreferenceConflicts } from './store/actions'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import {
|
||||
faDotCircle,
|
||||
faCheckSquare,
|
||||
faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faDotCircle, faCheckSquare, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCircle, faSquare } from '@fortawesome/free-regular-svg-icons'
|
||||
import styles from './CdnConfigView.module.css'
|
||||
import sharedStyles from './App.module.css'
|
||||
import classnames from 'classnames'
|
||||
import has from 'lodash/has'
|
||||
import size from 'lodash/size'
|
||||
import { has, size, get } from 'lodash'
|
||||
import Alert from './Alert'
|
||||
import PropTypes from 'prop-types'
|
||||
import get from 'lodash/get'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
|
||||
const UNSPECIFIED = ''
|
||||
|
@ -29,20 +21,26 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
const compat = useOption('compat')
|
||||
const pseudoElements = useOption('pseudoElements')
|
||||
const isVersion6 = !!version.match(/^6\./)
|
||||
const isVersion7 = !!version.match(/^7\./)
|
||||
const isVersion5 = !isVersion6 && !isVersion7
|
||||
|
||||
const pendingOptions = useSelector(state => state.pendingOptions)
|
||||
const pendingOptionConflicts = useSelector(state => state.pendingOptionConflicts)
|
||||
const hasChecked = useSelector(state => state.preferenceConflictDetection.hasChecked)
|
||||
const preferenceCheckSuccess = useSelector(state => state.preferenceConflictDetection.success)
|
||||
const preferenceCheckMessage = useSelector(state => state.preferenceConflictDetection.message)
|
||||
const pendingOptions = useSelector((state) => state.pendingOptions)
|
||||
const pendingOptionConflicts = useSelector((state) => state.pendingOptionConflicts)
|
||||
const hasChecked = useSelector((state) => state.preferenceConflictDetection.hasChecked)
|
||||
const preferenceCheckSuccess = useSelector((state) => state.preferenceConflictDetection.success)
|
||||
const preferenceCheckMessage = useSelector((state) => state.preferenceConflictDetection.message)
|
||||
|
||||
const versionOptions = useSelector(state => {
|
||||
const { releases: { available, latest_version_5, latest_version_6 } } = state
|
||||
const versionOptions = useSelector((state) => {
|
||||
const {
|
||||
releases: { available, latest_version_5, latest_version_6, latest_version_7 }
|
||||
} = state
|
||||
|
||||
return available.reduce((acc, version) => {
|
||||
if (latest_version_5 === version) {
|
||||
acc[version] = `${version} (latest 5.x)`
|
||||
} else if (latest_version_6 === version) {
|
||||
acc[version] = `${version} (latest 6.x)`
|
||||
} else if (latest_version_7 === version) {
|
||||
acc[version] = `${version} (latest)`
|
||||
} else {
|
||||
acc[version] = version
|
||||
|
@ -69,21 +67,31 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
function getDetectionStatusForOption(option) {
|
||||
if (has(pendingOptions, option)) {
|
||||
if (hasChecked && !preferenceCheckSuccess) {
|
||||
return <Alert title={ __( 'Error checking preferences', 'font-awesome' ) } type='warning'>
|
||||
return (
|
||||
<Alert
|
||||
title={__('Error checking preferences', 'font-awesome')}
|
||||
type="warning"
|
||||
>
|
||||
<p>{preferenceCheckMessage}</p>
|
||||
</Alert>
|
||||
)
|
||||
} else if (has(pendingOptionConflicts, option)) {
|
||||
return <Alert title={ __( 'Preference Conflict', 'font-awesome' ) } type='warning'>
|
||||
{
|
||||
size(pendingOptionConflicts[option]) > 1
|
||||
? <div>
|
||||
return (
|
||||
<Alert
|
||||
title={__('Preference Conflict', 'font-awesome')}
|
||||
type="warning"
|
||||
>
|
||||
{size(pendingOptionConflicts[option]) > 1 ? (
|
||||
<div>
|
||||
{__('This change might cause problems for these themes or plugins', 'font-awesome')}: {pendingOptionConflicts[option].join(', ')}.
|
||||
</div>
|
||||
: <div>
|
||||
) : (
|
||||
<div>
|
||||
{__('This change might cause problems for the theme or plugin', 'font-awesome')}: {pendingOptionConflicts[option][0]}.
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</Alert>
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
@ -92,8 +100,9 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
}
|
||||
}
|
||||
|
||||
return <div className={ classnames(styles['options-setter']) }>
|
||||
<form onSubmit={ e => e.preventDefault() }>
|
||||
return (
|
||||
<div className={classnames(styles['options-setter'])}>
|
||||
<form onSubmit={(e) => e.preventDefault()}>
|
||||
<div className={classnames(sharedStyles['flex'], sharedStyles['flex-row'])}>
|
||||
<div className={styles['option-header']}>Icons</div>
|
||||
<div className={styles['option-choice-container']}>
|
||||
|
@ -107,7 +116,10 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
onChange={() => handleOptionChange({ usePro: true })}
|
||||
className={classnames(sharedStyles['sr-only'], sharedStyles['input-radio-custom'])}
|
||||
/>
|
||||
<label htmlFor="code_edit_icons_pro" className={ styles['option-label'] }>
|
||||
<label
|
||||
htmlFor="code_edit_icons_pro"
|
||||
className={styles['option-label']}
|
||||
>
|
||||
<span className={sharedStyles['relative']}>
|
||||
<FontAwesomeIcon
|
||||
icon={faDotCircle}
|
||||
|
@ -122,9 +134,7 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
fixedWidth
|
||||
/>
|
||||
</span>
|
||||
<span className={ styles['option-label-text'] }>
|
||||
Pro
|
||||
</span>
|
||||
<span className={styles['option-label-text']}>Pro</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className={styles['option-choice']}>
|
||||
|
@ -136,7 +146,10 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
onChange={() => handleOptionChange({ usePro: false })}
|
||||
className={classnames(sharedStyles['sr-only'], sharedStyles['input-radio-custom'])}
|
||||
/>
|
||||
<label htmlFor="code_edit_icons_free" className={ styles['option-label'] }>
|
||||
<label
|
||||
htmlFor="code_edit_icons_free"
|
||||
className={styles['option-label']}
|
||||
>
|
||||
<span className={sharedStyles['relative']}>
|
||||
<FontAwesomeIcon
|
||||
icon={faDotCircle}
|
||||
|
@ -151,32 +164,57 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
className={sharedStyles['unchecked-icon']}
|
||||
/>
|
||||
</span>
|
||||
<span className={ styles['option-label-text'] }>
|
||||
Free
|
||||
</span>
|
||||
<span className={styles['option-label-text']}>Free</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{ usePro &&
|
||||
isVersion6 &&
|
||||
<Alert title={ __( 'Heads up! Pro Version 6 is not available from CDN', 'font-awesome' ) } type='warning'>
|
||||
<p>You can, however, use a Kit. Make sure you have a paid subscription and select "Use a Kit" above. We'll walk you through the other details from there.</p>
|
||||
{usePro && (isVersion6 || isVersion7) && (
|
||||
<Alert
|
||||
title={isVersion6 ? __('Heads up! Pro Version 6 is not available from CDN', 'font-awesome') : __('Heads up! Pro Version 7 is not available from CDN', 'font-awesome')}
|
||||
type="warning"
|
||||
>
|
||||
<p>
|
||||
You can, however, use a Kit. Make sure you have an active Font Awesome subscription and select "Use a Kit" above. We'll walk you through the
|
||||
other details from there.
|
||||
</p>
|
||||
</Alert>
|
||||
}
|
||||
{ usePro &&
|
||||
!isVersion6 &&
|
||||
<Alert title={ __( 'Heads up! Pro requires a Font Awesome subscription', 'font-awesome' ) } type='info'>
|
||||
)}
|
||||
{usePro && isVersion5 && (
|
||||
<Alert
|
||||
title={__('Heads up! Pro requires a Font Awesome subscription', 'font-awesome')}
|
||||
type="info"
|
||||
>
|
||||
<p>And you need to add your WordPress site to the allowed domains for your CDN.</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a rel="noopener noreferrer" target="_blank" href="https://fontawesome.com/account/cdn">{ __( 'Manage my allowed domains', 'font-awesome' ) }<FontAwesomeIcon icon={faExternalLinkAlt} style={{marginLeft: '.5em'}} /></a>
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href="https://fontawesome.com/account/cdn"
|
||||
>
|
||||
{__('Manage my allowed domains', 'font-awesome')}
|
||||
<FontAwesomeIcon
|
||||
icon={faExternalLinkAlt}
|
||||
style={{ marginLeft: '.5em' }}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a rel="noopener noreferrer" target="_blank" href="https://fontawesome.com/pro">{ __( 'Get Pro', 'font-awesome' ) }<FontAwesomeIcon icon={faExternalLinkAlt} style={{marginLeft: '.5em'}} /></a>
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href="https://fontawesome.com/pro"
|
||||
>
|
||||
{__('Get Pro', 'font-awesome')}
|
||||
<FontAwesomeIcon
|
||||
icon={faExternalLinkAlt}
|
||||
style={{ marginLeft: '.5em' }}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</Alert>
|
||||
}
|
||||
)}
|
||||
{getDetectionStatusForOption('usePro')}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -194,7 +232,10 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
onChange={() => handleOptionChange({ technology: 'svg' })}
|
||||
className={classnames(sharedStyles['sr-only'], sharedStyles['input-radio-custom'])}
|
||||
/>
|
||||
<label htmlFor="code_edit_tech_svg" className={ styles['option-label'] }>
|
||||
<label
|
||||
htmlFor="code_edit_tech_svg"
|
||||
className={styles['option-label']}
|
||||
>
|
||||
<span className={sharedStyles['relative']}>
|
||||
<FontAwesomeIcon
|
||||
icon={faDotCircle}
|
||||
|
@ -209,9 +250,7 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
fixedWidth
|
||||
/>
|
||||
</span>
|
||||
<span className={ styles['option-label-text'] }>
|
||||
{ __( 'SVG', 'font-awesome' ) }
|
||||
</span>
|
||||
<span className={styles['option-label-text']}>{__('SVG', 'font-awesome')}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className={styles['option-choice']}>
|
||||
|
@ -220,13 +259,18 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
name="code_edit_tech"
|
||||
type="radio"
|
||||
checked={technology === 'webfont'}
|
||||
onChange={ () => handleOptionChange({
|
||||
onChange={() =>
|
||||
handleOptionChange({
|
||||
technology: 'webfont',
|
||||
pseudoElements: false
|
||||
}) }
|
||||
})
|
||||
}
|
||||
className={classnames(sharedStyles['sr-only'], sharedStyles['input-radio-custom'])}
|
||||
/>
|
||||
<label htmlFor="code_edit_tech_webfont" className={ styles['option-label'] }>
|
||||
<label
|
||||
htmlFor="code_edit_tech_webfont"
|
||||
className={styles['option-label']}
|
||||
>
|
||||
<span className={sharedStyles['relative']}>
|
||||
<FontAwesomeIcon
|
||||
icon={faDotCircle}
|
||||
|
@ -243,12 +287,11 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
</span>
|
||||
<span className={styles['option-label-text']}>
|
||||
{__('Web Font', 'font-awesome')}
|
||||
{
|
||||
technology === 'webfont' &&
|
||||
{technology === 'webfont' && (
|
||||
<span className={styles['option-label-explanation']}>
|
||||
{__('CSS Pseudo-elements are enabled by default with Web Font', 'font-awesome')}
|
||||
</span>
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -258,8 +301,11 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
</div>
|
||||
<div className={classnames(sharedStyles['flex'], sharedStyles['flex-row'])}>
|
||||
<div className={styles['option-header']}></div>
|
||||
<div className={ styles['option-choice-container'] } style={{marginTop: '1em'}}>
|
||||
{ technology === 'svg' &&
|
||||
<div
|
||||
className={styles['option-choice-container']}
|
||||
style={{ marginTop: '1em' }}
|
||||
>
|
||||
{technology === 'svg' && (
|
||||
<>
|
||||
<input
|
||||
id="code_edit_features_pseudo_elements"
|
||||
|
@ -269,7 +315,10 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
onChange={() => handleOptionChange({ pseudoElements: !pseudoElements })}
|
||||
className={classnames(sharedStyles['sr-only'], sharedStyles['input-checkbox-custom'])}
|
||||
/>
|
||||
<label htmlFor="code_edit_features_pseudo_elements" className={styles['option-label']}>
|
||||
<label
|
||||
htmlFor="code_edit_features_pseudo_elements"
|
||||
className={styles['option-label']}
|
||||
>
|
||||
<span className={sharedStyles['relative']}>
|
||||
<FontAwesomeIcon
|
||||
icon={faCheckSquare}
|
||||
|
@ -287,15 +336,25 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
<span className={styles['option-label-text']}>
|
||||
{__('Enable CSS Pseudo-elements with SVG', 'font-awesome')}
|
||||
<span className={styles['option-label-explanation']}>
|
||||
{ __( 'May cause performance issues.', 'font-awesome' ) } <a rel="noopener noreferrer" target="_blank" style={{marginLeft: '.5em'}} href="https://fontawesome.com/how-to-use/on-the-web/advanced/css-pseudo-elements">
|
||||
{ __( 'Learn more', 'font-awesome' ) } <FontAwesomeIcon icon={faExternalLinkAlt} style={{marginLeft: '.5em'}} />
|
||||
{__('May cause performance issues.', 'font-awesome')}{' '}
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
style={{ marginLeft: '.5em' }}
|
||||
href="https://fontawesome.com/how-to-use/on-the-web/advanced/css-pseudo-elements"
|
||||
>
|
||||
{__('Learn more', 'font-awesome')}{' '}
|
||||
<FontAwesomeIcon
|
||||
icon={faExternalLinkAlt}
|
||||
style={{ marginLeft: '.5em' }}
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
{getDetectionStatusForOption('pseudoElements')}
|
||||
</>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<hr className={styles['option-divider']} />
|
||||
|
@ -306,16 +365,19 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
<select
|
||||
className={styles['version-select']}
|
||||
name="version"
|
||||
onChange={ e => handleOptionChange({ version: e.target.value }) }
|
||||
onChange={(e) => handleOptionChange({ version: e.target.value })}
|
||||
value={version}
|
||||
>
|
||||
{Object.keys(versionOptions).map((version, index) => {
|
||||
return (
|
||||
<option
|
||||
key={index}
|
||||
value={version}
|
||||
>
|
||||
{
|
||||
Object.keys(versionOptions).map((version, index) => {
|
||||
return <option key={ index } value={ version }>
|
||||
{version === UNSPECIFIED ? '-' : versionOptions[version]}
|
||||
</option>
|
||||
})
|
||||
}
|
||||
)
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
{getDetectionStatusForOption('version')}
|
||||
|
@ -336,7 +398,10 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
onChange={() => handleOptionChange({ compat: !compat })}
|
||||
className={classnames(sharedStyles['sr-only'], sharedStyles['input-radio-custom'])}
|
||||
/>
|
||||
<label htmlFor="code_edit_compat_on" className={ styles['option-label'] }>
|
||||
<label
|
||||
htmlFor="code_edit_compat_on"
|
||||
className={styles['option-label']}
|
||||
>
|
||||
<span className={sharedStyles['relative']}>
|
||||
<FontAwesomeIcon
|
||||
icon={faDotCircle}
|
||||
|
@ -351,9 +416,7 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
fixedWidth
|
||||
/>
|
||||
</span>
|
||||
<span className={ styles['option-label-text'] }>
|
||||
{ __( 'On', 'font-awesome' ) }
|
||||
</span>
|
||||
<span className={styles['option-label-text']}>{__('On', 'font-awesome')}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className={styles['option-choice']}>
|
||||
|
@ -366,7 +429,10 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
onChange={() => handleOptionChange({ compat: !compat })}
|
||||
className={classnames(sharedStyles['sr-only'], sharedStyles['input-radio-custom'])}
|
||||
/>
|
||||
<label htmlFor="code_edit_v4_compat_off" className={ styles['option-label'] }>
|
||||
<label
|
||||
htmlFor="code_edit_v4_compat_off"
|
||||
className={styles['option-label']}
|
||||
>
|
||||
<span className={sharedStyles['relative']}>
|
||||
<FontAwesomeIcon
|
||||
icon={faDotCircle}
|
||||
|
@ -381,9 +447,7 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
className={sharedStyles['unchecked-icon']}
|
||||
/>
|
||||
</span>
|
||||
<span className={ styles['option-label-text'] }>
|
||||
{ __( 'Off', 'font-awesome' ) }
|
||||
</span>
|
||||
<span className={styles['option-label-text']}>{__('Off', 'font-awesome')}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -392,6 +456,7 @@ export default function CdnConfigView({ useOption, handleSubmit }) {
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
CdnConfigView.propTypes = {
|
||||
|
|
|
@ -7,8 +7,14 @@ import { faSpinner } from '@fortawesome/free-solid-svg-icons'
|
|||
import { __ } from '@wordpress/i18n'
|
||||
|
||||
export default function CheckingOptionStatusIndicator() {
|
||||
return <span className={ styles['checking-option-status-indicator'] }>
|
||||
<FontAwesomeIcon spin className={ classnames(sharedStyles['icon']) } icon={ faSpinner }/>
|
||||
return (
|
||||
<span className={styles['checking-option-status-indicator']}>
|
||||
<FontAwesomeIcon
|
||||
spin
|
||||
className={classnames(sharedStyles['icon'])}
|
||||
icon={faSpinner}
|
||||
/>
|
||||
{__('checking for preference conflicts', 'font-awesome')}...
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,9 +2,7 @@ import React from 'react'
|
|||
import { useSelector } from 'react-redux'
|
||||
import styles from './ClientPreferencesView.module.css'
|
||||
import sharedStyles from './App.module.css'
|
||||
import find from 'lodash/find'
|
||||
import has from 'lodash/has'
|
||||
import size from 'lodash/size'
|
||||
import { find, has, size } from 'lodash'
|
||||
import classnames from 'classnames'
|
||||
import { __, sprintf } from '@wordpress/i18n'
|
||||
|
||||
|
@ -12,7 +10,7 @@ const UNSPECIFIED_INDICATOR = '-'
|
|||
|
||||
function formatVersionPreference(versionPreference = []) {
|
||||
return versionPreference
|
||||
.map(pref => `${pref[1]}${pref[0]}`)
|
||||
.map((pref) => `${pref[1]}${pref[0]}`)
|
||||
.join(
|
||||
sprintf(
|
||||
/* translators: 1: space */
|
||||
|
@ -23,31 +21,31 @@ function formatVersionPreference(versionPreference = []) {
|
|||
}
|
||||
|
||||
export default function ClientPreferencesView() {
|
||||
const clientPreferences = useSelector(state => state.clientPreferences)
|
||||
const conflicts = useSelector(state => state.preferenceConflicts)
|
||||
const clientPreferences = useSelector((state) => state.clientPreferences)
|
||||
const conflicts = useSelector((state) => state.preferenceConflicts)
|
||||
const hasAdditionalClients = size(clientPreferences)
|
||||
const hasConflicts = size(conflicts)
|
||||
|
||||
return <div className={ styles['client-requirements'] }>
|
||||
return (
|
||||
<div className={styles['client-requirements']}>
|
||||
<h3 className={sharedStyles['section-title']}>{__('Registered themes or plugins', 'font-awesome')}</h3>
|
||||
{
|
||||
hasAdditionalClients
|
||||
?
|
||||
{hasAdditionalClients ? (
|
||||
<div>
|
||||
<p className={sharedStyles['explanation']}>
|
||||
{
|
||||
__(
|
||||
{__(
|
||||
'Below is the list of active themes or plugins using Font Awesome that have opted-in to share information about the settings they are expecting.',
|
||||
'font-awesome'
|
||||
)
|
||||
}
|
||||
)}
|
||||
|
||||
{ hasConflicts
|
||||
? <span className={sharedStyles['explanation']}>
|
||||
{ __( 'The highlights show where the settings are mismatched. You might want to adjust your settings to match, or your icons may not work as expected.', 'font-awesome' ) }
|
||||
{hasConflicts ? (
|
||||
<span className={sharedStyles['explanation']}>
|
||||
{__(
|
||||
'The highlights show where the settings are mismatched. You might want to adjust your settings to match, or your icons may not work as expected.',
|
||||
'font-awesome'
|
||||
)}
|
||||
</span>
|
||||
: null
|
||||
}</p>
|
||||
) : null}
|
||||
</p>
|
||||
<table className={classnames('widefat', 'striped')}>
|
||||
<thead>
|
||||
<tr className={sharedStyles['table-header']}>
|
||||
|
@ -60,59 +58,36 @@ export default function ClientPreferencesView() {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
Object.values(clientPreferences).map((client, index) => {
|
||||
const clientHasConflict = optionName => !!find(conflicts[optionName], c => c === client.name)
|
||||
{Object.values(clientPreferences).map((client, index) => {
|
||||
const clientHasConflict = (optionName) => !!find(conflicts[optionName], (c) => c === client.name)
|
||||
|
||||
return <tr key={ index }>
|
||||
return (
|
||||
<tr key={index}>
|
||||
<td>{client.name}</td>
|
||||
<td
|
||||
className={
|
||||
classnames({ [styles.conflicted]: clientHasConflict('usePro') })
|
||||
}>
|
||||
{ has(client, 'usePro')
|
||||
? client.usePro ? 'Pro' : 'Free'
|
||||
: UNSPECIFIED_INDICATOR
|
||||
}
|
||||
<td className={classnames({ [styles.conflicted]: clientHasConflict('usePro') })}>
|
||||
{has(client, 'usePro') ? (client.usePro ? 'Pro' : 'Free') : UNSPECIFIED_INDICATOR}
|
||||
</td>
|
||||
<td
|
||||
className={ classnames({ [styles.conflicted]: clientHasConflict('technology') }) }>
|
||||
{ has(client, 'technology')
|
||||
? client.technology
|
||||
: UNSPECIFIED_INDICATOR
|
||||
}
|
||||
<td className={classnames({ [styles.conflicted]: clientHasConflict('technology') })}>
|
||||
{has(client, 'technology') ? client.technology : UNSPECIFIED_INDICATOR}
|
||||
</td>
|
||||
<td
|
||||
className={ classnames({ [styles.conflicted]: clientHasConflict('version') }) }>
|
||||
{ has(client, 'version')
|
||||
? formatVersionPreference(client.version)
|
||||
: UNSPECIFIED_INDICATOR
|
||||
}
|
||||
<td className={classnames({ [styles.conflicted]: clientHasConflict('version') })}>
|
||||
{has(client, 'version') ? formatVersionPreference(client.version) : UNSPECIFIED_INDICATOR}
|
||||
</td>
|
||||
<td
|
||||
className={ classnames({ [styles.conflicted]: clientHasConflict('compat') }) }>
|
||||
{ has(client, 'compat')
|
||||
? client.compat ? 'true' : 'false'
|
||||
: UNSPECIFIED_INDICATOR
|
||||
}
|
||||
<td className={classnames({ [styles.conflicted]: clientHasConflict('compat') })}>
|
||||
{has(client, 'compat') ? (client.compat ? 'true' : 'false') : UNSPECIFIED_INDICATOR}
|
||||
</td>
|
||||
<td
|
||||
className={ classnames({ [styles.conflicted]: clientHasConflict('pseudoElements') }) }>
|
||||
{ has(client, 'pseudoElements')
|
||||
? client.pseudoElements ? 'true' : 'false'
|
||||
: UNSPECIFIED_INDICATOR
|
||||
}
|
||||
<td className={classnames({ [styles.conflicted]: clientHasConflict('pseudoElements') })}>
|
||||
{has(client, 'pseudoElements') ? (client.pseudoElements ? 'true' : 'false') : UNSPECIFIED_INDICATOR}
|
||||
</td>
|
||||
</tr>
|
||||
})
|
||||
}
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
:
|
||||
<p className={ sharedStyles['explanation'] }>
|
||||
{ __( 'No active themes or plugins have requested preferences for Font Awesome.', 'font-awesome' ) }
|
||||
</p>
|
||||
}
|
||||
) : (
|
||||
<p className={sharedStyles['explanation']}>{__('No active themes or plugins have requested preferences for Font Awesome.', 'font-awesome')}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,8 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|||
import { faCheckCircle, faCog, faExclamationTriangle, faGrin, faSkull, faThumbsUp, faTimesCircle } from '@fortawesome/free-solid-svg-icons'
|
||||
import { ADMIN_TAB_TROUBLESHOOT } from './store/reducers'
|
||||
import ConflictDetectionTimer from './ConflictDetectionTimer'
|
||||
import size from 'lodash/size'
|
||||
import has from 'lodash/has'
|
||||
import { has, size } from 'lodash'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import ErrorBoundary from './ErrorBoundary'
|
||||
|
||||
|
@ -147,67 +146,53 @@ const STYLES = {
|
|||
function withErrorBoundary(Component) {
|
||||
return class extends ErrorBoundary {
|
||||
render() {
|
||||
return <div style={ STYLES.container }>
|
||||
{
|
||||
!!this.state.error
|
||||
? <div style={ STYLES.badness }>
|
||||
return (
|
||||
<div style={STYLES.container}>
|
||||
{!!this.state.error ? (
|
||||
<div style={STYLES.badness}>
|
||||
<FontAwesomeIcon icon={faExclamationTriangle} />
|
||||
{
|
||||
__( ' Whoops, this is embarrassing! Some unexpected error has occurred. There might be some additional diagnostic information in the JavaScript console.', 'font-awesome' )
|
||||
}
|
||||
{__(
|
||||
' Whoops, this is embarrassing! Some unexpected error has occurred. There might be some additional diagnostic information in the JavaScript console.',
|
||||
'font-awesome'
|
||||
)}
|
||||
</div>
|
||||
: <Component />
|
||||
}
|
||||
) : (
|
||||
<Component />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ConflictDetectionReporter() {
|
||||
const dispatch = useDispatch()
|
||||
const settingsPageUrl = useSelector(state => state.settingsPageUrl)
|
||||
const settingsPageUrl = useSelector((state) => state.settingsPageUrl)
|
||||
const troubleshootTabUrl = `${settingsPageUrl}&tab=ts`
|
||||
const activeAdminTab = useSelector(state => state.activeAdminTab )
|
||||
const activeAdminTab = useSelector((state) => state.activeAdminTab)
|
||||
const currentlyOnPluginAdminPage = window.location.href.startsWith(settingsPageUrl)
|
||||
const currentlyOnTroubleshootTab = currentlyOnPluginAdminPage && activeAdminTab === ADMIN_TAB_TROUBLESHOOT
|
||||
const userAttemptedToStopScanner = useSelector(state => state.userAttemptedToStopScanner)
|
||||
const userAttemptedToStopScanner = useSelector((state) => state.userAttemptedToStopScanner)
|
||||
|
||||
const unregisteredClients = useSelector(
|
||||
state => state.unregisteredClients
|
||||
)
|
||||
const unregisteredClients = useSelector((state) => state.unregisteredClients)
|
||||
|
||||
const unregisteredClientsBeforeDetection = useSelector(
|
||||
state => state.unregisteredClientDetectionStatus.unregisteredClientsBeforeDetection
|
||||
)
|
||||
const unregisteredClientsBeforeDetection = useSelector((state) => state.unregisteredClientDetectionStatus.unregisteredClientsBeforeDetection)
|
||||
|
||||
const recentConflictsDetected = useSelector(
|
||||
state => state.unregisteredClientDetectionStatus.recentConflictsDetected
|
||||
)
|
||||
const recentConflictsDetected = useSelector((state) => state.unregisteredClientDetectionStatus.recentConflictsDetected)
|
||||
|
||||
const expired = useSelector(
|
||||
state => !state.showConflictDetectionReporter
|
||||
)
|
||||
const expired = useSelector((state) => !state.showConflictDetectionReporter)
|
||||
|
||||
const restarting = useSelector(
|
||||
state => expired && state.conflictDetectionScannerStatus.isSubmitting
|
||||
)
|
||||
const restarting = useSelector((state) => expired && state.conflictDetectionScannerStatus.isSubmitting)
|
||||
|
||||
const scannerReady = useSelector(
|
||||
state => state.conflictDetectionScannerStatus.hasSubmitted && state.conflictDetectionScannerStatus.success
|
||||
)
|
||||
const scannerReady = useSelector((state) => state.conflictDetectionScannerStatus.hasSubmitted && state.conflictDetectionScannerStatus.success)
|
||||
|
||||
const scannerIsStopping = useSelector(
|
||||
state => userAttemptedToStopScanner
|
||||
&& !state.conflictDetectionScannerStatus.hasSubmitted
|
||||
)
|
||||
const scannerIsStopping = useSelector((state) => userAttemptedToStopScanner && !state.conflictDetectionScannerStatus.hasSubmitted)
|
||||
|
||||
const userStoppedScannerSuccessfully = useSelector(
|
||||
state => userAttemptedToStopScanner
|
||||
&& !scannerIsStopping
|
||||
&& state.conflictDetectionScannerStatus.success
|
||||
(state) => userAttemptedToStopScanner && !scannerIsStopping && state.conflictDetectionScannerStatus.success
|
||||
)
|
||||
|
||||
const runStatus = useSelector(state => {
|
||||
const runStatus = useSelector((state) => {
|
||||
const { isSubmitting, hasSubmitted, success } = state.unregisteredClientDetectionStatus
|
||||
if (userAttemptedToStopScanner) {
|
||||
if (scannerIsStopping) {
|
||||
|
@ -238,37 +223,50 @@ function ConflictDetectionReporter() {
|
|||
}
|
||||
})
|
||||
|
||||
const errorMessage = useSelector(
|
||||
state => state.unregisteredClientDetectionStatus.message
|
||||
)
|
||||
const errorMessage = useSelector((state) => state.unregisteredClientDetectionStatus.message)
|
||||
|
||||
function stopScanner() {
|
||||
dispatch(userAttemptToStopScanner())
|
||||
dispatch(setConflictDetectionScanner({ enable: false }))
|
||||
}
|
||||
|
||||
const expiredOrStoppedDiv =
|
||||
const expiredOrStoppedDiv = (
|
||||
<div>
|
||||
<h2 style={ STYLES.tally }><span>{ size( unregisteredClients ) }</span> <span> { __( 'Results to Review', 'font-awesome' ) }</span></h2>
|
||||
<h2 style={STYLES.tally}>
|
||||
<span>{size(unregisteredClients)}</span> <span> {__('Results to Review', 'font-awesome')}</span>
|
||||
</h2>
|
||||
<p style={STYLES.p}>
|
||||
{
|
||||
currentlyOnTroubleshootTab
|
||||
? __( 'Manage results or restart the scanner here on the Troubleshoot tab.', 'font-awesome' )
|
||||
: <>
|
||||
{
|
||||
__( 'Manage results or restart the scanner on the Troubleshoot tab.', 'font-awesome' )
|
||||
} <a href={ troubleshootTabUrl } style={ STYLES.link }>{ __('Go', 'font-awesome' ) }</a>
|
||||
{currentlyOnTroubleshootTab ? (
|
||||
__('Manage results or restart the scanner here on the Troubleshoot tab.', 'font-awesome')
|
||||
) : (
|
||||
<>
|
||||
{__('Manage results or restart the scanner on the Troubleshoot tab.', 'font-awesome')}{' '}
|
||||
<a
|
||||
href={troubleshootTabUrl}
|
||||
style={STYLES.link}
|
||||
>
|
||||
{__('Go', 'font-awesome')}
|
||||
</a>
|
||||
</>
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
const stoppingOrSubmittingDiv =
|
||||
const stoppingOrSubmittingDiv = (
|
||||
<div>
|
||||
<div style={STYLES.status}>
|
||||
<h2 style={ STYLES.h2 }><FontAwesomeIcon icon={ faCog } size="sm" spin /> <span>{ runStatus.display }</span></h2>
|
||||
<h2 style={STYLES.h2}>
|
||||
<FontAwesomeIcon
|
||||
icon={faCog}
|
||||
size="sm"
|
||||
spin
|
||||
/>{' '}
|
||||
<span>{runStatus.display}</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -277,68 +275,125 @@ function ConflictDetectionReporter() {
|
|||
<p style={STYLES.adminEyesOnly}>{__('only admins can see this box', 'font-awesome')}</p>
|
||||
</div>
|
||||
<div style={STYLES.content}>
|
||||
|
||||
{
|
||||
{
|
||||
None:
|
||||
None: (
|
||||
<div>
|
||||
<div style={STYLES.status}>
|
||||
<h2 style={ STYLES.h2 }><FontAwesomeIcon icon={ faGrin } size="sm" /> <span>{ __( 'All clear!', 'font-awesome' ) }</span></h2>
|
||||
<h2 style={STYLES.h2}>
|
||||
<FontAwesomeIcon
|
||||
icon={faGrin}
|
||||
size="sm"
|
||||
/>{' '}
|
||||
<span>{__('All clear!', 'font-awesome')}</span>
|
||||
</h2>
|
||||
<p style={STYLES.p}>{__('No new conflicts found on this page.', 'font-awesome')}</p>
|
||||
</div>
|
||||
</div>,
|
||||
Running:
|
||||
</div>
|
||||
),
|
||||
Running: (
|
||||
<div>
|
||||
<div style={STYLES.status}>
|
||||
<h2 style={ STYLES.h2 }><FontAwesomeIcon icon={ faCog } size="sm" spin /> <span>{ __( 'Scanning', 'font-awesome' ) }...</span></h2>
|
||||
<h2 style={STYLES.h2}>
|
||||
<FontAwesomeIcon
|
||||
icon={faCog}
|
||||
size="sm"
|
||||
spin
|
||||
/>{' '}
|
||||
<span>{__('Scanning', 'font-awesome')}...</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>,
|
||||
Restarting:
|
||||
</div>
|
||||
),
|
||||
Restarting: (
|
||||
<div>
|
||||
<div style={STYLES.status}>
|
||||
<h2 style={ STYLES.h2 }><FontAwesomeIcon icon={ faCog } size="sm" spin /> <span>{ __( 'Restarting', 'font-awesome' ) }...</span></h2>
|
||||
<h2 style={STYLES.h2}>
|
||||
<FontAwesomeIcon
|
||||
icon={faCog}
|
||||
size="sm"
|
||||
spin
|
||||
/>{' '}
|
||||
<span>{__('Restarting', 'font-awesome')}...</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>,
|
||||
Ready:
|
||||
</div>
|
||||
),
|
||||
Ready: (
|
||||
<div>
|
||||
<div>
|
||||
<h2 style={ STYLES.h2 }><FontAwesomeIcon icon={ faThumbsUp } size="sm" /> { __( 'Proton pack charged!', 'font-awesome' ) }</h2>
|
||||
<h2 style={STYLES.h2}>
|
||||
<FontAwesomeIcon
|
||||
icon={faThumbsUp}
|
||||
size="sm"
|
||||
/>{' '}
|
||||
{__('Proton pack charged!', 'font-awesome')}
|
||||
</h2>
|
||||
<p style={STYLES.p}>{__('Wander through the pages of your web site and this scanner will track progress.', 'font-awesome')}</p>
|
||||
</div>
|
||||
</div>,
|
||||
</div>
|
||||
),
|
||||
Submitting: stoppingOrSubmittingDiv,
|
||||
Stopping: stoppingOrSubmittingDiv,
|
||||
Done:
|
||||
Done: (
|
||||
<div>
|
||||
<div style={STYLES.status}>
|
||||
<h2 style={ STYLES.h2 }><FontAwesomeIcon icon={ faCheckCircle } size="sm" /> <span>{ __( 'Page scan complete', 'font-awesome' ) }</span></h2>
|
||||
<h2 style={STYLES.h2}>
|
||||
<FontAwesomeIcon
|
||||
icon={faCheckCircle}
|
||||
size="sm"
|
||||
/>{' '}
|
||||
<span>{__('Page scan complete', 'font-awesome')}</span>
|
||||
</h2>
|
||||
</div>
|
||||
<p style={ STYLES.tally }><span style={ STYLES.count }>{ size( Object.keys( recentConflictsDetected ).filter(k => ! has(unregisteredClientsBeforeDetection, k) ) ) }</span> <span>{ __( 'new conflicts found on this page', 'font-awesome' ) }</span></p>
|
||||
<p style={ STYLES.tally }><span style={ STYLES.count }>{ size( unregisteredClients ) }</span> <span>total found</span>
|
||||
{
|
||||
currentlyOnTroubleshootTab ?
|
||||
<span> ({ __( 'manage conflicts here on the Troubleshoot tab', 'font-awesome' ) })</span>
|
||||
: <span> (<a href={ troubleshootTabUrl } style={ STYLES.link }>{ __( 'manage', 'font-awesome' ) }</a>)</span>
|
||||
}
|
||||
<p style={STYLES.tally}>
|
||||
<span style={STYLES.count}>{size(Object.keys(recentConflictsDetected).filter((k) => !has(unregisteredClientsBeforeDetection, k)))}</span>{' '}
|
||||
<span>{__('new conflicts found on this page', 'font-awesome')}</span>
|
||||
</p>
|
||||
</div>,
|
||||
<p style={STYLES.tally}>
|
||||
<span style={STYLES.count}>{size(unregisteredClients)}</span> <span>total found</span>
|
||||
{currentlyOnTroubleshootTab ? (
|
||||
<span> ({__('manage conflicts here on the Troubleshoot tab', 'font-awesome')})</span>
|
||||
) : (
|
||||
<span>
|
||||
(
|
||||
<a
|
||||
href={troubleshootTabUrl}
|
||||
style={STYLES.link}
|
||||
>
|
||||
{__('manage', 'font-awesome')}
|
||||
</a>
|
||||
)
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
Expired: expiredOrStoppedDiv,
|
||||
Stopped: expiredOrStoppedDiv,
|
||||
Error:
|
||||
Error: (
|
||||
<div>
|
||||
<h2 style={ STYLES.h2 }><FontAwesomeIcon icon={ faSkull } /> <span>{ __( 'Don\'t cross the streams! It would be bad.', 'font-awesome' ) }</span></h2>
|
||||
<p style={ STYLES.p }>
|
||||
{ errorMessage }
|
||||
</p>
|
||||
<h2 style={STYLES.h2}>
|
||||
<FontAwesomeIcon icon={faSkull} /> <span>{__("Don't cross the streams! It would be bad.", 'font-awesome')}</span>
|
||||
</h2>
|
||||
<p style={STYLES.p}>{errorMessage}</p>
|
||||
</div>
|
||||
)
|
||||
}[runStatus.code]
|
||||
}
|
||||
</div>
|
||||
<div style={STYLES.timerRow}>
|
||||
<span>
|
||||
<ConflictDetectionTimer addDescription>
|
||||
<button style={ STYLES.button } title={ __( 'Stop timer', 'font-awesome' ) } onClick={() => stopScanner()}>
|
||||
<FontAwesomeIcon icon={ faTimesCircle } size="lg" />
|
||||
<button
|
||||
style={STYLES.button}
|
||||
title={__('Stop timer', 'font-awesome')}
|
||||
onClick={() => stopScanner()}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faTimesCircle}
|
||||
size="lg"
|
||||
/>
|
||||
</button>
|
||||
</ConflictDetectionTimer>
|
||||
</span>
|
||||
|
|
|
@ -11,11 +11,11 @@ import createInterpolateElement from './createInterpolateElement'
|
|||
|
||||
export default function ConflictDetectionScannerSection() {
|
||||
const dispatch = useDispatch()
|
||||
const detectConflictsUntil = useSelector(state => state.detectConflictsUntil)
|
||||
const nowMs = (new Date()).valueOf()
|
||||
const detectingConflicts = (new Date(detectConflictsUntil * 1000)) > nowMs
|
||||
const { isSubmitting, hasSubmitted, message, success } = useSelector(state => state.conflictDetectionScannerStatus)
|
||||
const showConflictDetectionReporter = useSelector(state => state.showConflictDetectionReporter)
|
||||
const detectConflictsUntil = useSelector((state) => state.detectConflictsUntil)
|
||||
const nowMs = new Date().valueOf()
|
||||
const detectingConflicts = new Date(detectConflictsUntil * 1000) > nowMs
|
||||
const { isSubmitting, hasSubmitted, message, success } = useSelector((state) => state.conflictDetectionScannerStatus)
|
||||
const showConflictDetectionReporter = useSelector((state) => state.showConflictDetectionReporter)
|
||||
const store = useStore()
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -31,51 +31,64 @@ export default function ConflictDetectionScannerSection() {
|
|||
}
|
||||
}, [showConflictDetectionReporter, store])
|
||||
|
||||
return <div>
|
||||
return (
|
||||
<div>
|
||||
<h2 className={sharedStyles['section-title']}>{__('Detect Conflicts with Other Versions of Font Awesome', 'font-awesome')}</h2>
|
||||
<div className={sharedStyles['explanation']}>
|
||||
<p>
|
||||
{ __( 'If you are having trouble loading Font Awesome icons on your WordPress site, it may be because other themes or plugins are loading conflicting versions of Font Awesome. You can use our conflict scanner to detect other versions of Font Awesome running on your site.', 'font-awesome' ) }
|
||||
{__(
|
||||
'If you are having trouble loading Font Awesome icons on your WordPress site, it may be because other themes or plugins are loading conflicting versions of Font Awesome. You can use our conflict scanner to detect other versions of Font Awesome running on your site.',
|
||||
'font-awesome'
|
||||
)}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{createInterpolateElement(
|
||||
__(
|
||||
'Enable the scanner below and a box will appear in the bottom corner of your window while it runs for 10 minutes (only you and other admins can see the box). While the scanner is running, browse your site, especially the pages having trouble to catch any <noWrap>Slimers - *ahem* - conflicts</noWrap> in the scanner.',
|
||||
'font-awesome'
|
||||
),
|
||||
{
|
||||
createInterpolateElement(
|
||||
__( 'Enable the scanner below and a box will appear in the bottom corner of your window while it runs for 10 minutes (only you and other admins can see the box). While the scanner is running, browse your site, especially the pages having trouble to catch any <noWrap>Slimers - *ahem* - conflicts</noWrap> in the scanner.', 'font-awesome' ),
|
||||
{
|
||||
noWrap: <span style={{ whiteSpace: "nowrap" }} />
|
||||
}
|
||||
)
|
||||
noWrap: <span style={{ whiteSpace: 'nowrap' }} />
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className={sharedStyles['scanner-actions']}>
|
||||
{
|
||||
detectingConflicts
|
||||
? <button className={sharedStyles['faPrimary']} disabled >
|
||||
{detectingConflicts ? (
|
||||
<button
|
||||
className={sharedStyles['faPrimary']}
|
||||
disabled
|
||||
>
|
||||
{__('Scanner running', 'font-awesome')}: <ConflictDetectionTimer />
|
||||
</button>
|
||||
: <button className="button button-primary" disabled={ isSubmitting } onClick={() => dispatch(setConflictDetectionScanner({ enable: true }))}>
|
||||
{
|
||||
sprintf(
|
||||
__( 'Enable scanner for %d minutes', 'font-awesome' ),
|
||||
CONFLICT_DETECTION_SCANNER_DURATION_MIN
|
||||
)
|
||||
}
|
||||
) : (
|
||||
<button
|
||||
className="button button-primary"
|
||||
disabled={isSubmitting}
|
||||
onClick={() => dispatch(setConflictDetectionScanner({ enable: true }))}
|
||||
>
|
||||
{sprintf(__('Enable scanner for %d minutes', 'font-awesome'), CONFLICT_DETECTION_SCANNER_DURATION_MIN)}
|
||||
</button>
|
||||
}
|
||||
)}
|
||||
<div className={sharedStyles['scanner-runstatus']}>
|
||||
{
|
||||
isSubmitting
|
||||
? <FontAwesomeIcon icon={ faSpinner } spin />
|
||||
: hasSubmitted
|
||||
? success
|
||||
? <FontAwesomeIcon icon={ faCheck } />
|
||||
: <><FontAwesomeIcon icon={ faSkull } /> <span>{ message }</span></>
|
||||
: null
|
||||
}
|
||||
{isSubmitting ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faSpinner}
|
||||
spin
|
||||
/>
|
||||
) : hasSubmitted ? (
|
||||
success ? (
|
||||
<FontAwesomeIcon icon={faCheck} />
|
||||
) : (
|
||||
<>
|
||||
<FontAwesomeIcon icon={faSkull} /> <span>{message}</span>
|
||||
</>
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<hr className={sharedStyles['section-divider']} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,8 +2,7 @@ import React, { useState, useEffect } from 'react'
|
|||
import PropTypes from 'prop-types'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import sharedStyles from './App.module.css'
|
||||
import padStart from 'lodash/padStart'
|
||||
import dropWhile from 'lodash/dropWhile'
|
||||
import { padStart, dropWhile } from 'lodash'
|
||||
import { __, sprintf } from '@wordpress/i18n'
|
||||
|
||||
const SECONDS_PER_DAY = 60 * 60 * 24
|
||||
|
@ -12,7 +11,7 @@ const SECONDS_PER_MINUTE = 60
|
|||
|
||||
export function timerString(durationSeconds) {
|
||||
const days = Math.floor(durationSeconds / SECONDS_PER_DAY)
|
||||
const hours = Math.floor((durationSeconds - (days * SECONDS_PER_DAY)) / SECONDS_PER_HOUR)
|
||||
const hours = Math.floor((durationSeconds - days * SECONDS_PER_DAY) / SECONDS_PER_HOUR)
|
||||
const minutes = Math.floor((durationSeconds - (days * SECONDS_PER_DAY + hours * SECONDS_PER_HOUR)) / SECONDS_PER_MINUTE)
|
||||
const seconds = durationSeconds - (days * SECONDS_PER_DAY + hours * SECONDS_PER_HOUR + minutes * SECONDS_PER_MINUTE)
|
||||
|
||||
|
@ -25,19 +24,19 @@ export function timerString(durationSeconds) {
|
|||
}
|
||||
return acc
|
||||
}, []),
|
||||
part => part.match(/^[0]+$/)
|
||||
(part) => part.match(/^[0]+$/)
|
||||
).join(':')
|
||||
}
|
||||
|
||||
function secondsRemaining(endTime) {
|
||||
const now = Math.floor((new Date()) / 1000)
|
||||
const now = Math.floor(new Date() / 1000)
|
||||
const remaining = endTime - now
|
||||
|
||||
return remaining < 0 ? 0 : remaining
|
||||
}
|
||||
|
||||
export default function ConflictDetectionTimer({ addDescription, children }) {
|
||||
const detectConflictsUntil = useSelector(state => state.detectConflictsUntil)
|
||||
const detectConflictsUntil = useSelector((state) => state.detectConflictsUntil)
|
||||
const [timeRemaining, setTimer] = useState(secondsRemaining(detectConflictsUntil))
|
||||
const dispatch = useDispatch()
|
||||
|
||||
|
@ -56,22 +55,18 @@ export default function ConflictDetectionTimer({ addDescription, children }) {
|
|||
return () => timeoutId && clearTimeout(timeoutId)
|
||||
}, [detectConflictsUntil, timeRemaining, dispatch])
|
||||
|
||||
return timeRemaining <= 0 ? null : <span className={ sharedStyles['conflict-detection-timer'] }>
|
||||
return timeRemaining <= 0 ? null : (
|
||||
<span className={sharedStyles['conflict-detection-timer']}>
|
||||
{timerString(timeRemaining)}
|
||||
{
|
||||
!!addDescription &&
|
||||
(
|
||||
timeRemaining > 60
|
||||
/* translators: 1: space */
|
||||
? sprintf( __( '%1$sminutes left to browse your site for trouble', 'font-awesome' ), ' ' )
|
||||
/* translators: 1: space */
|
||||
: sprintf( __( '%1$sseconds left to browse your site for trouble', 'font-awesome' ), ' ' )
|
||||
)
|
||||
}
|
||||
{
|
||||
children
|
||||
}
|
||||
{!!addDescription &&
|
||||
(timeRemaining > 60
|
||||
? /* translators: 1: space */
|
||||
sprintf(__('%1$sminutes left to browse your site for trouble', 'font-awesome'), ' ')
|
||||
: /* translators: 1: space */
|
||||
sprintf(__('%1$sseconds left to browse your site for trouble', 'font-awesome'), ' '))}
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
ConflictDetectionTimer.propTypes = {
|
||||
|
|
|
@ -3,18 +3,17 @@ import styles from './ErrorFallbackView.module.css'
|
|||
import Alert from './Alert'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
|
||||
export const fatalAlert = <Alert title={ __( 'Whoops, this is embarrassing', 'font-awesome' ) } type='warning'>
|
||||
<p>
|
||||
{
|
||||
__( 'Some unexpected error has occurred. There might be some additional diagnostic information in the JavaScript console.', 'font-awesome' )
|
||||
}
|
||||
</p>
|
||||
export const fatalAlert = (
|
||||
<Alert
|
||||
title={__('Whoops, this is embarrassing', 'font-awesome')}
|
||||
type="warning"
|
||||
>
|
||||
<p>{__('Some unexpected error has occurred. There might be some additional diagnostic information in the JavaScript console.', 'font-awesome')}</p>
|
||||
</Alert>
|
||||
)
|
||||
|
||||
function ErrorFallbackView() {
|
||||
return <div className={ styles['error-fallback'] }>
|
||||
{ fatalAlert }
|
||||
</div>
|
||||
return <div className={styles['error-fallback']}>{fatalAlert}</div>
|
||||
}
|
||||
|
||||
export default ErrorFallbackView
|
||||
|
|
|
@ -9,7 +9,7 @@ import { setActiveAdminTab } from './store/actions'
|
|||
import { __ } from '@wordpress/i18n'
|
||||
|
||||
export default function FontAwesomeAdminView() {
|
||||
const activeAdminTab = useSelector(state => state.activeAdminTab || ADMIN_TAB_SETTINGS )
|
||||
const activeAdminTab = useSelector((state) => state.activeAdminTab || ADMIN_TAB_SETTINGS)
|
||||
const dispatch = useDispatch()
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
import React from 'react'
|
||||
import { mount } from 'enzyme'
|
||||
import { Provider } from 'react-redux'
|
||||
import { createStore } from './store'
|
||||
import ErrorBoundary from './ErrorBoundary'
|
||||
import FontAwesomeAdminView from './FontAwesomeAdminView'
|
||||
|
||||
describe('FontAwesomeAdminView', () => {
|
||||
test('mounts successfully', () => {
|
||||
const initialData = JSON.parse(
|
||||
'{"apiNonce":"deadbeef42","apiUrl":"http://localhost:8765/index.php?rest_route=/font-awesome/v1","unregisteredClients":[],"showConflictDetectionReporter":"","settingsPageUrl":"http://localhost:8765/wp-admin/options-general.php?page=font-awesome","detectConflictsUntil":0,"options":{"usePro":false,"compat":true,"technology":"webfont","pseudoElements":false,"version":"5.12.0"},"showAdmin":"1","onSettingsPage":"1","clientPreferences":{"beta-plugin":{"name":"beta-plugin","compat":true}},"releases":{"available":["5.12.0","5.11.2","5.11.1","5.11.0","5.10.2","5.10.1","5.10.0","5.9.0","5.8.2","5.8.1","5.8.0","5.7.2","5.7.1","5.7.0","5.6.3","5.6.1","5.6.0","5.5.0","5.4.2","5.4.1","5.3.1","5.2.0","5.1.1","5.1.0","5.0.13","5.0.12","5.0.10","5.0.9","5.0.8","5.0.6","5.0.4","5.0.3","5.0.2","5.0.1"],"latest_version_5":"5.12.0","latest_version_6":"6.1.1"},"pluginVersion":"4.0.0-rc13","preferenceConflicts":[],"v3DeprecationWarning":""}'
|
||||
)
|
||||
|
||||
global['__FontAwesomeOfficialPlugin__'] = initialData
|
||||
|
||||
const store = createStore(initialData)
|
||||
|
||||
const wrapper = mount(
|
||||
<ErrorBoundary>
|
||||
<Provider store={ store }>
|
||||
<FontAwesomeAdminView/>
|
||||
</Provider>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
|
||||
expect(wrapper).toBeTruthy()
|
||||
})
|
||||
})
|
|
@ -5,70 +5,57 @@ import styles from './KitSelectView.module.css'
|
|||
import PropTypes from 'prop-types'
|
||||
import { useSelector } from 'react-redux'
|
||||
import Alert from './Alert'
|
||||
import get from 'lodash/get'
|
||||
import has from 'lodash/has'
|
||||
import size from 'lodash/size'
|
||||
import { get, has, size } from 'lodash'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import createInterpolateElement from './createInterpolateElement'
|
||||
|
||||
export default function KitConfigView({ kitToken }) {
|
||||
const kitTokenIsActive = useSelector(state => get(state, 'options.kitToken') === kitToken)
|
||||
const kitTokenApiData = useSelector(state => (state.kits || []).find(k => k.token === kitToken))
|
||||
const pendingOptionConflicts = useSelector(state => state.pendingOptionConflicts)
|
||||
const hasChecked = useSelector(state => state.preferenceConflictDetection.hasChecked)
|
||||
const preferenceCheckSuccess = useSelector(state => state.preferenceConflictDetection.success)
|
||||
const kitTokenIsActive = useSelector((state) => get(state, 'options.kitToken') === kitToken)
|
||||
const kitTokenApiData = useSelector((state) => (state.kits || []).find((k) => k.token === kitToken))
|
||||
const pendingOptionConflicts = useSelector((state) => state.pendingOptionConflicts)
|
||||
const hasChecked = useSelector((state) => state.preferenceConflictDetection.hasChecked)
|
||||
const preferenceCheckSuccess = useSelector((state) => state.preferenceConflictDetection.success)
|
||||
|
||||
const technology = useSelector(state =>
|
||||
kitTokenIsActive
|
||||
? state.options.technology
|
||||
: kitTokenApiData.technologySelected === 'svg'
|
||||
? 'svg'
|
||||
: 'webfont'
|
||||
)
|
||||
const technology = useSelector((state) => (kitTokenIsActive ? state.options.technology : kitTokenApiData.technologySelected === 'svg' ? 'svg' : 'webfont'))
|
||||
|
||||
const usePro = useSelector(state =>
|
||||
kitTokenIsActive
|
||||
? state.options.usePro
|
||||
: kitTokenApiData.licenseSelected === 'pro'
|
||||
)
|
||||
const usePro = useSelector((state) => (kitTokenIsActive ? state.options.usePro : kitTokenApiData.licenseSelected === 'pro'))
|
||||
|
||||
const compat = useSelector(state =>
|
||||
kitTokenIsActive
|
||||
? state.options.compat
|
||||
: kitTokenApiData.shimEnabled
|
||||
)
|
||||
const compat = useSelector((state) => (kitTokenIsActive ? state.options.compat : kitTokenApiData.shimEnabled))
|
||||
|
||||
const version = useSelector(state =>
|
||||
kitTokenIsActive
|
||||
? state.options.version
|
||||
: kitTokenApiData.version
|
||||
)
|
||||
const version = useSelector((state) => (kitTokenIsActive ? state.options.version : kitTokenApiData.version))
|
||||
|
||||
function getDetectionStatusForOption(option) {
|
||||
if (hasChecked && preferenceCheckSuccess && has(pendingOptionConflicts, option)) {
|
||||
return <Alert title={ __( 'Preference Conflict', 'font-awesome' ) } type='warning'>
|
||||
{
|
||||
size(pendingOptionConflicts[option]) > 1
|
||||
? <div>
|
||||
return (
|
||||
<Alert
|
||||
title={__('Preference Conflict', 'font-awesome')}
|
||||
type="warning"
|
||||
>
|
||||
{size(pendingOptionConflicts[option]) > 1 ? (
|
||||
<div>
|
||||
{__('This change might cause problems for these themes or plugins:', 'font-awesome')} {pendingOptionConflicts[option].join(', ')}.
|
||||
</div>
|
||||
: <div>
|
||||
) : (
|
||||
<div>
|
||||
{__('This change might cause problems for the theme or plugin:', 'font-awesome')} {pendingOptionConflicts[option][0]}.
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</Alert>
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return (!kitTokenIsActive && !kitTokenApiData)
|
||||
? <Alert type="warning" title={ __('Oh no! We could not find the kit data for the selected kit token.', 'font-awesome' )}>
|
||||
{
|
||||
__( 'Try reloading.', 'font-awesome' )
|
||||
}
|
||||
return !kitTokenIsActive && !kitTokenApiData ? (
|
||||
<Alert
|
||||
type="warning"
|
||||
title={__('Oh no! We could not find the kit data for the selected kit token.', 'font-awesome')}
|
||||
>
|
||||
{__('Try reloading.', 'font-awesome')}
|
||||
</Alert>
|
||||
: <div className={ styles['kit-config-view-container'] }>
|
||||
) : (
|
||||
<div className={styles['kit-config-view-container']}>
|
||||
<table className={styles['selected-kit-settings']}>
|
||||
<tbody>
|
||||
<tr>
|
||||
|
@ -102,18 +89,25 @@ export default function KitConfigView({ kitToken }) {
|
|||
</tbody>
|
||||
</table>
|
||||
<p className={styles['tip-text']}>
|
||||
{
|
||||
createInterpolateElement(
|
||||
__( 'Make changes on <a>fontawesome.com/kits <externalLinkIcon/></a>', 'font-awesome' ),
|
||||
{
|
||||
{createInterpolateElement(__('Make changes on <a>fontawesome.com/kits <externalLinkIcon/></a>', 'font-awesome'), {
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
a: <a target="_blank" rel="noopener noreferrer" href="https://fontawesome.com/kits" />,
|
||||
externalLinkIcon: <FontAwesomeIcon icon={faExternalLinkAlt} style={{marginLeft: '.5em'}} />
|
||||
}
|
||||
a: (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://fontawesome.com/kits"
|
||||
/>
|
||||
),
|
||||
externalLinkIcon: (
|
||||
<FontAwesomeIcon
|
||||
icon={faExternalLinkAlt}
|
||||
style={{ marginLeft: '.5em' }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
KitConfigView.propTypes = {
|
||||
|
|
|
@ -1,47 +1,34 @@
|
|||
import React, { createRef, useState, useEffect } from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import Alert from './Alert'
|
||||
import {
|
||||
resetPendingOptions,
|
||||
queryKits,
|
||||
addPendingOption,
|
||||
checkPreferenceConflicts,
|
||||
updateApiToken,
|
||||
resetOptionsFormState
|
||||
} from './store/actions'
|
||||
import { resetPendingOptions, queryKits, addPendingOption, checkPreferenceConflicts, updateApiToken, resetOptionsFormState } from './store/actions'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import {
|
||||
faSpinner,
|
||||
faSync,
|
||||
faExternalLinkAlt,
|
||||
faRedo,
|
||||
faSkull,
|
||||
faTrashAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faSpinner, faSync, faExternalLinkAlt, faRedo, faSkull, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faQuestionCircle, faCheckCircle } from '@fortawesome/free-regular-svg-icons'
|
||||
import styles from './KitSelectView.module.css'
|
||||
import sharedStyles from './App.module.css'
|
||||
import classnames from 'classnames'
|
||||
import PropTypes from 'prop-types'
|
||||
import size from 'lodash/size'
|
||||
import { size } from 'lodash'
|
||||
import { sprintf, __ } from '@wordpress/i18n'
|
||||
|
||||
export default function KitSelectView({ useOption, masterSubmitButtonShowing, setMasterSubmitButtonShowing }) {
|
||||
const dispatch = useDispatch()
|
||||
const kitTokenActive = useSelector(state => state.options.kitToken)
|
||||
const kitTokenActive = useSelector((state) => state.options.kitToken)
|
||||
const kitToken = useOption('kitToken')
|
||||
const [pendingApiToken, setPendingApiToken] = useState(null)
|
||||
const [showingRemoveApiTokenAlert, setShowRemoveApiTokenAlert] = useState(false)
|
||||
const [showApiTokenInputForUpdate, setShowApiTokenInputForUpdate] = useState(false)
|
||||
const apiToken = useSelector(state => {
|
||||
const apiToken = useSelector((state) => {
|
||||
if (null !== pendingApiToken) return pendingApiToken
|
||||
|
||||
return state.options.apiToken
|
||||
})
|
||||
const kits = useSelector( state => state.kits ) || []
|
||||
const hasSubmitted = useSelector(state => state.optionsFormState.hasSubmitted)
|
||||
const submitSuccess = useSelector(state => state.optionsFormState.success)
|
||||
const submitMessage = useSelector(state => state.optionsFormState.message)
|
||||
const isSubmitting = useSelector(state => state.optionsFormState.isSubmitting)
|
||||
const kits = useSelector((state) => state.kits) || []
|
||||
const hasSubmitted = useSelector((state) => state.optionsFormState.hasSubmitted)
|
||||
const submitSuccess = useSelector((state) => state.optionsFormState.success)
|
||||
const submitMessage = useSelector((state) => state.optionsFormState.message)
|
||||
const isSubmitting = useSelector((state) => state.optionsFormState.isSubmitting)
|
||||
|
||||
function removeApiToken() {
|
||||
if (!!kitTokenActive) {
|
||||
|
@ -66,22 +53,18 @@ export default function KitSelectView({ useOption, masterSubmitButtonShowing, se
|
|||
return
|
||||
}
|
||||
|
||||
const selectedKit = (kits || []).find(k => k.token === kitToken)
|
||||
const selectedKit = (kits || []).find((k) => k.token === kitToken)
|
||||
|
||||
if (!selectedKit) {
|
||||
throw new Error(
|
||||
sprintf(
|
||||
__( 'When selecting to use kit %s, somehow the information we needed was missing. Try reloading the page.' ),
|
||||
kitToken
|
||||
)
|
||||
)
|
||||
throw new Error(sprintf(__('When selecting to use kit %s, somehow the information we needed was missing. Try reloading the page.'), kitToken))
|
||||
}
|
||||
|
||||
if (kitTokenActive === kitToken) {
|
||||
// We're just resetting back to the state we were in
|
||||
dispatch(resetPendingOptions())
|
||||
} else {
|
||||
dispatch(addPendingOption({
|
||||
dispatch(
|
||||
addPendingOption({
|
||||
kitToken,
|
||||
technology: 'svg' === selectedKit.technologySelected ? 'svg' : 'webfont',
|
||||
usePro: 'pro' === selectedKit.licenseSelected,
|
||||
|
@ -90,13 +73,14 @@ export default function KitSelectView({ useOption, masterSubmitButtonShowing, se
|
|||
// At the time this is being implemented, kits don't yet support
|
||||
// toggling pseudoElement support for SVG, but it's implicitly supported for webfont.
|
||||
pseudoElements: 'svg' !== selectedKit.technologySelected
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
dispatch(checkPreferenceConflicts())
|
||||
}
|
||||
|
||||
const kitsQueryStatus = useSelector(state => state.kitsQueryStatus)
|
||||
const kitsQueryStatus = useSelector((state) => state.kitsQueryStatus)
|
||||
|
||||
/**
|
||||
* This seems like a lot of effort just to keep the focus on the API Token input
|
||||
|
@ -118,8 +102,7 @@ export default function KitSelectView({ useOption, masterSubmitButtonShowing, se
|
|||
}
|
||||
})
|
||||
|
||||
|
||||
const hasSavedApiToken = useSelector(state => !! state.options.apiToken)
|
||||
const hasSavedApiToken = useSelector((state) => !!state.options.apiToken)
|
||||
|
||||
function cancelApiTokenUpdate() {
|
||||
setShowApiTokenInputForUpdate(false)
|
||||
|
@ -128,7 +111,6 @@ export default function KitSelectView({ useOption, masterSubmitButtonShowing, se
|
|||
}
|
||||
|
||||
function ApiTokenInput() {
|
||||
|
||||
useEffect(() => {
|
||||
if (submitSuccess && showApiTokenInputForUpdate) {
|
||||
setShowApiTokenInputForUpdate(false)
|
||||
|
@ -136,10 +118,15 @@ export default function KitSelectView({ useOption, masterSubmitButtonShowing, se
|
|||
}
|
||||
})
|
||||
|
||||
return <>
|
||||
return (
|
||||
<>
|
||||
<div className={classnames(styles['field-apitoken'], { [styles['api-token-update']]: showApiTokenInputForUpdate })}>
|
||||
<label htmlFor="api_token">
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={ faQuestionCircle } size="lg" />
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faQuestionCircle}
|
||||
size="lg"
|
||||
/>
|
||||
{__('API Token', 'font-awesome')}
|
||||
</label>
|
||||
<div>
|
||||
|
@ -150,15 +137,27 @@ export default function KitSelectView({ useOption, masterSubmitButtonShowing, se
|
|||
ref={apiTokenInputRef}
|
||||
value={pendingApiToken || ''}
|
||||
size="20"
|
||||
onChange={ e => {
|
||||
onChange={(e) => {
|
||||
setApiTokenInputHasFocus(true)
|
||||
setPendingApiToken(e.target.value)
|
||||
}}
|
||||
/>
|
||||
|
||||
<p>
|
||||
{ __( 'Grab your secure and unique API token from your Font Awesome account page and enter it here so we can securely fetch your kits.', 'font-awesome') } <a target="_blank" rel="noopener noreferrer" href="https://fontawesome.com/account#api-tokens">
|
||||
{ __( 'Get your API token on fontawesome.com', 'font-awesome') } <FontAwesomeIcon icon={faExternalLinkAlt} style={{marginLeft: '.5em'}} />
|
||||
{__(
|
||||
'Grab your secure and unique API token from your Font Awesome account page and enter it here so we can securely fetch your kits.',
|
||||
'font-awesome'
|
||||
)}{' '}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://fontawesome.com/account#api-tokens"
|
||||
>
|
||||
{__('Get your API token on fontawesome.com', 'font-awesome')}{' '}
|
||||
<FontAwesomeIcon
|
||||
icon={faExternalLinkAlt}
|
||||
style={{ marginLeft: '.5em' }}
|
||||
/>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -174,32 +173,39 @@ export default function KitSelectView({ useOption, masterSubmitButtonShowing, se
|
|||
onMouseDown={() => {
|
||||
dispatch(updateApiToken({ apiToken: pendingApiToken, runQueryKits: true }))
|
||||
setPendingApiToken(null)
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{
|
||||
(hasSubmitted && ! submitSuccess) &&
|
||||
{hasSubmitted && !submitSuccess && (
|
||||
<div className={classnames(sharedStyles['submit-status'], sharedStyles['fail'])}>
|
||||
<div className={classnames(sharedStyles['fail-icon-container'])}>
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={ faSkull } />
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faSkull}
|
||||
/>
|
||||
</div>
|
||||
<div className={ sharedStyles['explanation'] }>
|
||||
{ submitMessage }
|
||||
<div className={sharedStyles['explanation']}>{submitMessage}</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
isSubmitting &&
|
||||
)}
|
||||
{isSubmitting && (
|
||||
<span className={classnames(sharedStyles['submit-status'], sharedStyles['submitting'])}>
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={faSpinner} spin/>
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faSpinner}
|
||||
spin
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
{
|
||||
(showApiTokenInputForUpdate && ! isSubmitting) &&
|
||||
<button onClick={ () => cancelApiTokenUpdate() } className={ styles['button-dismissable'] }>{ __('Nevermind', 'font-awesome') }</button>
|
||||
}
|
||||
)}
|
||||
{showApiTokenInputForUpdate && !isSubmitting && (
|
||||
<button
|
||||
onClick={() => cancelApiTokenUpdate()}
|
||||
className={styles['button-dismissable']}
|
||||
>
|
||||
{__('Nevermind', 'font-awesome')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function ApiTokenControl() {
|
||||
|
@ -210,40 +216,67 @@ export default function KitSelectView({ useOption, masterSubmitButtonShowing, se
|
|||
setShowRemoveApiTokenAlert(false)
|
||||
}
|
||||
|
||||
return <div className={ styles['api-token-control-wrapper'] }>
|
||||
return (
|
||||
<div className={styles['api-token-control-wrapper']}>
|
||||
<div className={classnames(styles['api-token-control'], { [styles['api-token-update']]: showApiTokenInputForUpdate })}>
|
||||
{
|
||||
showApiTokenInputForUpdate
|
||||
? <ApiTokenInput />
|
||||
: <>
|
||||
{showApiTokenInputForUpdate ? (
|
||||
<ApiTokenInput />
|
||||
) : (
|
||||
<>
|
||||
<p className={styles['token-saved']}>
|
||||
<span>
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={ faCheckCircle } size="lg" />
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faCheckCircle}
|
||||
size="lg"
|
||||
/>
|
||||
</span>
|
||||
{__('API Token Saved', 'font-awesome')}
|
||||
</p>
|
||||
{
|
||||
!!apiToken &&
|
||||
{!!apiToken && (
|
||||
<div className={styles['button-group']}>
|
||||
<button onClick={ () => switchToApiTokenUpdate() } className={ styles['refresh'] } type="button">
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={ faSync } title="update" alt="update" />
|
||||
<button
|
||||
onClick={() => switchToApiTokenUpdate()}
|
||||
className={styles['refresh']}
|
||||
type="button"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faSync}
|
||||
title="update"
|
||||
alt="update"
|
||||
/>
|
||||
<span>{__('Update token', 'font-awesome')}</span>
|
||||
</button>
|
||||
<button onClick={ () => removeApiToken() } className={ styles['remove'] } type="button"><FontAwesomeIcon className={ sharedStyles['icon'] } icon={ faTrashAlt } title="remove" alt="remove" /></button>
|
||||
<button
|
||||
onClick={() => removeApiToken()}
|
||||
className={styles['remove']}
|
||||
type="button"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faTrashAlt}
|
||||
title="remove"
|
||||
alt="remove"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
{
|
||||
showingRemoveApiTokenAlert &&
|
||||
{showingRemoveApiTokenAlert && (
|
||||
<div className={styles['api-token-control-alert-wrapper']}>
|
||||
<Alert title={ __( 'Whoa, whoa, whoa!', 'font-awesome' ) } type='warning'>
|
||||
<Alert
|
||||
title={__('Whoa, whoa, whoa!', 'font-awesome')}
|
||||
type="warning"
|
||||
>
|
||||
{__('You can\'t remove your API token when "Use a Kit" is active. Switch to "Use CDN" first.', 'font-awesome')}
|
||||
</Alert>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const STATUS = {
|
||||
|
@ -257,8 +290,7 @@ export default function KitSelectView({ useOption, masterSubmitButtonShowing, se
|
|||
}
|
||||
|
||||
function KitSelector() {
|
||||
const status =
|
||||
apiToken
|
||||
const status = apiToken
|
||||
? kitsQueryStatus.isSubmitting
|
||||
? STATUS.querying
|
||||
: kitsQueryStatus.hasSubmitted
|
||||
|
@ -272,125 +304,164 @@ export default function KitSelectView({ useOption, masterSubmitButtonShowing, se
|
|||
: STATUS.apiTokenReadyNoKitsYet
|
||||
: STATUS.noApiToken
|
||||
|
||||
const kitRefreshButton = <button onClick={ () => dispatch(queryKits()) } className={ styles['refresh'] }>
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={ faRedo } title="refresh" alt="refresh" />
|
||||
<span>
|
||||
{
|
||||
0 === size(kits)
|
||||
? __( 'Get latest kits data', 'font-awesome' )
|
||||
: __( 'Refresh kits data', 'font-awesome' )
|
||||
}
|
||||
</span>
|
||||
const kitRefreshButton = (
|
||||
<button
|
||||
onClick={() => dispatch(queryKits())}
|
||||
className={styles['refresh']}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faRedo}
|
||||
title="refresh"
|
||||
alt="refresh"
|
||||
/>
|
||||
<span>{0 === size(kits) ? __('Get latest kits data', 'font-awesome') : __('Refresh kits data', 'font-awesome')}</span>
|
||||
</button>
|
||||
|
||||
const activeKitNotice = kitTokenActive
|
||||
? <div className={ styles['wrap-active-kit'] }><p className={ classnames(styles['active-kit'], styles['set']) }><FontAwesomeIcon className={ sharedStyles['icon'] } icon={ faCheckCircle } size="lg" />
|
||||
{
|
||||
sprintf(
|
||||
__( '%s Kit is Currently Active' ),
|
||||
kitTokenActive
|
||||
)
|
||||
}
|
||||
</p></div>
|
||||
: null
|
||||
|
||||
const activeKitNotice = kitTokenActive ? (
|
||||
<div className={styles['wrap-active-kit']}>
|
||||
<p className={classnames(styles['active-kit'], styles['set'])}>
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faCheckCircle}
|
||||
size="lg"
|
||||
/>
|
||||
{sprintf(__('%s Kit is Currently Active'), kitTokenActive)}
|
||||
</p>
|
||||
</div>
|
||||
) : null
|
||||
|
||||
return <div className={ styles['kit-selector-container'] }>
|
||||
|
||||
return (
|
||||
<div className={styles['kit-selector-container']}>
|
||||
{activeKitNotice}
|
||||
|
||||
<div className={styles['wrap-selectkit']}>
|
||||
<h3 className={ styles['title-selectkit'] }><FontAwesomeIcon className={ sharedStyles['icon'] } icon={ faQuestionCircle } size="lg" />
|
||||
<h3 className={styles['title-selectkit']}>
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faQuestionCircle}
|
||||
size="lg"
|
||||
/>
|
||||
{__('Pick a Kit to Use or Check Settings', 'font-awesome')}
|
||||
</h3>
|
||||
<div className={styles['selectkit']}>
|
||||
<p>
|
||||
{
|
||||
__( 'Refresh your kits data to get the latest kit settings, then select the kit you would like to use. Remember to save when you\'re ready to use it.', 'font-awesome' )
|
||||
}
|
||||
{__(
|
||||
"Refresh your kits data to get the latest kit settings, then select the kit you would like to use. Remember to save when you're ready to use it.",
|
||||
'font-awesome'
|
||||
)}
|
||||
</p>
|
||||
{
|
||||
{
|
||||
noApiToken: 'noApiToken',
|
||||
apiTokenReadyNoKitsYet: <>{ activeKitNotice } { kitRefreshButton }</>,
|
||||
querying:
|
||||
apiTokenReadyNoKitsYet: (
|
||||
<>
|
||||
{activeKitNotice} {kitRefreshButton}
|
||||
</>
|
||||
),
|
||||
querying: (
|
||||
<div>
|
||||
<span>
|
||||
{ __( 'Loading your kits...', 'font-awesome' ) }
|
||||
</span>
|
||||
<span>{__('Loading your kits...', 'font-awesome')}</span>
|
||||
<span className={classnames(sharedStyles['submit-status'], sharedStyles['submitting'])}>
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={faSpinner} spin/>
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faSpinner}
|
||||
spin
|
||||
/>
|
||||
</span>
|
||||
</div>,
|
||||
</div>
|
||||
),
|
||||
|
||||
networkError:
|
||||
networkError: (
|
||||
<div className={classnames(sharedStyles['submit-status'], sharedStyles['fail'])}>
|
||||
<div className={classnames(sharedStyles['fail-icon-container'])}>
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={ faSkull } />
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faSkull}
|
||||
/>
|
||||
</div>
|
||||
<div className={ sharedStyles['explanation'] }>
|
||||
{ kitsQueryStatus.message }
|
||||
<div className={sharedStyles['explanation']}>{kitsQueryStatus.message}</div>
|
||||
</div>
|
||||
</div>,
|
||||
),
|
||||
|
||||
noKitsFoundAfterQuery:
|
||||
noKitsFoundAfterQuery: (
|
||||
<>
|
||||
<Alert title="Zoinks! Looks like you don't have any kits set up yet." type="info">
|
||||
<Alert
|
||||
title="Zoinks! Looks like you don't have any kits set up yet."
|
||||
type="info"
|
||||
>
|
||||
<p>
|
||||
{ __( 'Head over to Font Awesome to create one, then come back here and refresh your kits.', 'font-awesome' ) } <a rel="noopener noreferrer" target="_blank" href="https://fontawesome.com/kits">
|
||||
{ __( 'Create a kit on Font Awesome', 'font-awesome' ) } <FontAwesomeIcon icon={faExternalLinkAlt} /></a>
|
||||
{__('Head over to Font Awesome to create one, then come back here and refresh your kits.', 'font-awesome')}{' '}
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href="https://fontawesome.com/kits"
|
||||
>
|
||||
{__('Create a kit on Font Awesome', 'font-awesome')} <FontAwesomeIcon icon={faExternalLinkAlt} />
|
||||
</a>
|
||||
</p>
|
||||
</Alert>
|
||||
{kitRefreshButton}
|
||||
</>,
|
||||
</>
|
||||
),
|
||||
|
||||
kitSelection:
|
||||
kitSelection: (
|
||||
<>
|
||||
<div className={styles['field-kitselect']}>
|
||||
<select
|
||||
className={styles['kit-select']}
|
||||
id="kits"
|
||||
name="kit"
|
||||
onChange={ e => handleKitChange({ kitToken: e.target.value }) }
|
||||
onChange={(e) => handleKitChange({ kitToken: e.target.value })}
|
||||
disabled={!masterSubmitButtonShowing}
|
||||
value={kitToken || ''}
|
||||
>
|
||||
<option key='empty' value=''>{ __( 'Select a kit', 'font-awesome' ) }</option>
|
||||
{
|
||||
kits.map((kit, index) => {
|
||||
return <option key={ index } value={ kit.token }>
|
||||
<option
|
||||
key="empty"
|
||||
value=""
|
||||
>
|
||||
{__('Select a kit', 'font-awesome')}
|
||||
</option>
|
||||
{kits.map((kit, index) => {
|
||||
return (
|
||||
<option
|
||||
key={index}
|
||||
value={kit.token}
|
||||
>
|
||||
{`${kit.name} (${kit.token})`}
|
||||
</option>
|
||||
})
|
||||
}
|
||||
)
|
||||
})}
|
||||
</select>
|
||||
{kitRefreshButton}
|
||||
</div>
|
||||
</>,
|
||||
|
||||
showingOnlyActiveKit:
|
||||
<>
|
||||
{ kitRefreshButton }
|
||||
</>
|
||||
),
|
||||
|
||||
showingOnlyActiveKit: <>{kitRefreshButton}</>
|
||||
}[status]
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return <div>
|
||||
return (
|
||||
<div>
|
||||
<div className={styles['kit-tab-content']}>
|
||||
{
|
||||
hasSavedApiToken
|
||||
? <>
|
||||
{hasSavedApiToken ? (
|
||||
<>
|
||||
<ApiTokenControl />
|
||||
<KitSelector />
|
||||
</>
|
||||
: <ApiTokenInput />
|
||||
}
|
||||
) : (
|
||||
<ApiTokenInput />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
KitSelectView.propTypes = {
|
||||
|
|
|
@ -7,18 +7,21 @@ import { __ } from '@wordpress/i18n'
|
|||
import createInterpolateElement from './createInterpolateElement'
|
||||
|
||||
export default function ManageFontAwesomeVersionsSection() {
|
||||
return <div className={ classnames(sharedStyles['explanation'], styles['font-awesome-versions-section']) }>
|
||||
return (
|
||||
<div className={classnames(sharedStyles['explanation'], styles['font-awesome-versions-section'])}>
|
||||
<h2 className={sharedStyles['section-title']}>{__('Versions of Font Awesome Active on Your Site', 'font-awesome')}</h2>
|
||||
<p>
|
||||
{
|
||||
createInterpolateElement(
|
||||
__( '<b>Registered plugins and themes</b> have opted to share information about the Font Awesome settings they are expecting, and are therefore easier to fix. For the <b>unregistered plugins and themes</b>, which are more unpredictable, we have provided options for you to block their Font Awesome source from loading and causing issues.', 'font-awesome' ),
|
||||
{createInterpolateElement(
|
||||
__(
|
||||
'<b>Registered plugins and themes</b> have opted to share information about the Font Awesome settings they are expecting, and are therefore easier to fix. For the <b>unregistered plugins and themes</b>, which are more unpredictable, we have provided options for you to block their Font Awesome source from loading and causing issues.',
|
||||
'font-awesome'
|
||||
),
|
||||
{
|
||||
b: <b />
|
||||
}
|
||||
)
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
<ClientPreferencesView />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,40 +6,30 @@ import KitConfigView from './KitConfigView'
|
|||
import sharedStyles from './App.module.css'
|
||||
import optionStyles from './CdnConfigView.module.css'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import {
|
||||
faDotCircle,
|
||||
faSpinner,
|
||||
faCheck,
|
||||
faSkull,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { faDotCircle, faSpinner, faCheck, faSkull } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCircle } from '@fortawesome/free-regular-svg-icons'
|
||||
import classnames from 'classnames'
|
||||
import styles from './SettingsTab.module.css'
|
||||
import has from 'lodash/has'
|
||||
import { has, size } from 'lodash'
|
||||
import { addPendingOption, submitPendingOptions, chooseAwayFromKitConfig, chooseIntoKitConfig } from './store/actions'
|
||||
import CheckingOptionStatusIndicator from './CheckingOptionsStatusIndicator'
|
||||
import size from 'lodash/size'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
|
||||
export default function SettingsTab() {
|
||||
const dispatch = useDispatch()
|
||||
const alreadyUsingKit = useSelector( state => !!state.options.kitToken )
|
||||
const alreadyUsingKit = useSelector((state) => !!state.options.kitToken)
|
||||
const [useKit, setUseKit] = useState(alreadyUsingKit)
|
||||
const isChecking = useSelector(state => state.preferenceConflictDetection.isChecking)
|
||||
const hasSubmitted = useSelector(state => state.optionsFormState.hasSubmitted)
|
||||
const submitSuccess = useSelector(state => state.optionsFormState.success)
|
||||
const submitMessage = useSelector(state => state.optionsFormState.message)
|
||||
const isSubmitting = useSelector(state => state.optionsFormState.isSubmitting)
|
||||
const pendingOptions = useSelector(state => state.pendingOptions)
|
||||
const apiToken = useSelector(state => state.options.apiToken)
|
||||
const isChecking = useSelector((state) => state.preferenceConflictDetection.isChecking)
|
||||
const hasSubmitted = useSelector((state) => state.optionsFormState.hasSubmitted)
|
||||
const submitSuccess = useSelector((state) => state.optionsFormState.success)
|
||||
const submitMessage = useSelector((state) => state.optionsFormState.message)
|
||||
const isSubmitting = useSelector((state) => state.optionsFormState.isSubmitting)
|
||||
const pendingOptions = useSelector((state) => state.pendingOptions)
|
||||
const apiToken = useSelector((state) => state.options.apiToken)
|
||||
const [masterSubmitButtonShowing, setMasterSubmitButtonShowing] = useState(true)
|
||||
|
||||
function useOption(option) {
|
||||
return useSelector(state =>
|
||||
has(state.pendingOptions, option)
|
||||
? state.pendingOptions[option]
|
||||
: state.options[option]
|
||||
)
|
||||
return useSelector((state) => (has(state.pendingOptions, option) ? state.pendingOptions[option] : state.options[option]))
|
||||
}
|
||||
|
||||
function handleSubmit(e) {
|
||||
|
@ -54,7 +44,7 @@ export default function SettingsTab() {
|
|||
const kitToken = useOption('kitToken')
|
||||
|
||||
// The one that's actually saved in the database already
|
||||
const activeKitToken = useSelector( state => state.options.kitToken )
|
||||
const activeKitToken = useSelector((state) => state.options.kitToken)
|
||||
|
||||
function handleOptionChange(change = {}) {
|
||||
dispatch(addPendingOption(change))
|
||||
|
@ -78,7 +68,9 @@ export default function SettingsTab() {
|
|||
dispatch(chooseIntoKitConfig())
|
||||
}
|
||||
|
||||
return <div><div className={ sharedStyles['wrapper-div'] }>
|
||||
return (
|
||||
<div>
|
||||
<div className={sharedStyles['wrapper-div']}>
|
||||
<h3>{__('How are you using Font Awesome?', 'font-awesome')}</h3>
|
||||
<div className={styles['select-config-container']}>
|
||||
<span>
|
||||
|
@ -91,7 +83,10 @@ export default function SettingsTab() {
|
|||
onChange={() => handleSwitchToKitConfig()}
|
||||
className={classnames(sharedStyles['sr-only'], sharedStyles['input-radio-custom'])}
|
||||
/>
|
||||
<label htmlFor="select_use_kits" className={ optionStyles['option-label'] }>
|
||||
<label
|
||||
htmlFor="select_use_kits"
|
||||
className={optionStyles['option-label']}
|
||||
>
|
||||
<span className={sharedStyles['relative']}>
|
||||
<FontAwesomeIcon
|
||||
icon={faDotCircle}
|
||||
|
@ -106,9 +101,7 @@ export default function SettingsTab() {
|
|||
fixedWidth
|
||||
/>
|
||||
</span>
|
||||
<span className={ optionStyles['option-label-text'] }>
|
||||
{ __( 'Use A Kit', 'font-awesome' ) }
|
||||
</span>
|
||||
<span className={optionStyles['option-label-text']}>{__('Use A Kit', 'font-awesome')}</span>
|
||||
</label>
|
||||
</span>
|
||||
<span>
|
||||
|
@ -121,7 +114,10 @@ export default function SettingsTab() {
|
|||
onChange={() => handleSwitchAwayFromKitConfig()}
|
||||
className={classnames(sharedStyles['sr-only'], sharedStyles['input-radio-custom'])}
|
||||
/>
|
||||
<label htmlFor="select_use_cdn" className={ optionStyles['option-label'] }>
|
||||
<label
|
||||
htmlFor="select_use_cdn"
|
||||
className={optionStyles['option-label']}
|
||||
>
|
||||
<span className={sharedStyles['relative']}>
|
||||
<FontAwesomeIcon
|
||||
icon={faDotCircle}
|
||||
|
@ -136,25 +132,32 @@ export default function SettingsTab() {
|
|||
fixedWidth
|
||||
/>
|
||||
</span>
|
||||
<span className={ optionStyles['option-label-text'] }>
|
||||
{ __( 'Use CDN', 'font-awesome' ) }
|
||||
</span>
|
||||
<span className={optionStyles['option-label-text']}>{__('Use CDN', 'font-awesome')}</span>
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
<>
|
||||
{
|
||||
useKit
|
||||
? <>
|
||||
<KitSelectView useOption={ useOption } handleOptionChange={ handleOptionChange } handleSubmit={ handleSubmit } masterSubmitButtonShowing={ masterSubmitButtonShowing } setMasterSubmitButtonShowing={ setMasterSubmitButtonShowing }/>
|
||||
{useKit ? (
|
||||
<>
|
||||
<KitSelectView
|
||||
useOption={useOption}
|
||||
handleOptionChange={handleOptionChange}
|
||||
handleSubmit={handleSubmit}
|
||||
masterSubmitButtonShowing={masterSubmitButtonShowing}
|
||||
setMasterSubmitButtonShowing={setMasterSubmitButtonShowing}
|
||||
/>
|
||||
{!!kitToken && <KitConfigView kitToken={kitToken} />}
|
||||
</>
|
||||
: <CdnConfigView useOption={ useOption } handleOptionChange={ handleOptionChange } handleSubmit={ handleSubmit }/>
|
||||
}
|
||||
) : (
|
||||
<CdnConfigView
|
||||
useOption={useOption}
|
||||
handleOptionChange={handleOptionChange}
|
||||
handleSubmit={handleSubmit}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
{
|
||||
(!useKit || ( apiToken && masterSubmitButtonShowing ) ) &&
|
||||
{(!useKit || (apiToken && masterSubmitButtonShowing)) && (
|
||||
<div className={classnames(sharedStyles['submit-wrapper'], ['submit'])}>
|
||||
<input
|
||||
type="submit"
|
||||
|
@ -165,33 +168,41 @@ export default function SettingsTab() {
|
|||
disabled={size(pendingOptions) === 0}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
{ hasSubmitted
|
||||
? submitSuccess
|
||||
? <span className={ classnames(sharedStyles['submit-status'], sharedStyles['success']) }>
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={ faCheck } />
|
||||
{hasSubmitted ? (
|
||||
submitSuccess ? (
|
||||
<span className={classnames(sharedStyles['submit-status'], sharedStyles['success'])}>
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faCheck}
|
||||
/>
|
||||
</span>
|
||||
: <div className={ classnames(sharedStyles['submit-status'], sharedStyles['fail']) }>
|
||||
) : (
|
||||
<div className={classnames(sharedStyles['submit-status'], sharedStyles['fail'])}>
|
||||
<div className={classnames(sharedStyles['fail-icon-container'])}>
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={ faSkull } />
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faSkull}
|
||||
/>
|
||||
</div>
|
||||
<div className={ sharedStyles['explanation'] }>
|
||||
{ submitMessage }
|
||||
<div className={sharedStyles['explanation']}>{submitMessage}</div>
|
||||
</div>
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
{
|
||||
isSubmitting
|
||||
? <span className={ classnames(sharedStyles['submit-status'], sharedStyles['submitting']) }>
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={faSpinner} spin/>
|
||||
)
|
||||
) : null}
|
||||
{isSubmitting ? (
|
||||
<span className={classnames(sharedStyles['submit-status'], sharedStyles['submitting'])}>
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faSpinner}
|
||||
spin
|
||||
/>
|
||||
</span>
|
||||
: isChecking
|
||||
? <CheckingOptionStatusIndicator/>
|
||||
: size(pendingOptions) > 0
|
||||
? <span className={ sharedStyles['submit-status'] }>{ __( 'you have pending changes', 'font-awesome' ) }</span>
|
||||
: null
|
||||
}
|
||||
) : isChecking ? (
|
||||
<CheckingOptionStatusIndicator />
|
||||
) : size(pendingOptions) > 0 ? (
|
||||
<span className={sharedStyles['submit-status']}>{__('you have pending changes', 'font-awesome')}</span>
|
||||
) : null}
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react'
|
||||
import ManageFontAwesomeVersionsSection from './ManageFontAwesomeVersionsSection'
|
||||
import UnregisteredClientsView from './UnregisteredClientsView'
|
||||
import V3DeprecationWarning from './V3DeprecationWarning'
|
||||
import ConflictDetectionScannerSection from './ConflictDetectionScannerSection'
|
||||
import sharedStyles from './App.module.css'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
|
@ -12,21 +11,17 @@ import {
|
|||
resetPendingBlocklistSubmissionStatus,
|
||||
resetUnregisteredClientsDeletionStatus
|
||||
} from './store/actions'
|
||||
import {
|
||||
faCheck,
|
||||
faSkull,
|
||||
faSpinner } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCheck, faSkull, faSpinner } from '@fortawesome/free-solid-svg-icons'
|
||||
import classnames from 'classnames'
|
||||
import size from 'lodash/size'
|
||||
import { size } from 'lodash'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
|
||||
export default function TroubleshootTab() {
|
||||
const dispatch = useDispatch()
|
||||
const hasV3DeprecationWarning = useSelector(state => !!state.v3DeprecationWarning)
|
||||
const unregisteredClients = useSelector(state => state.unregisteredClients)
|
||||
const unregisteredClients = useSelector((state) => state.unregisteredClients)
|
||||
|
||||
const blocklistUpdateStatus = useSelector(state => state.blocklistUpdateStatus)
|
||||
const unregisteredClientsDeletionStatus = useSelector(state => state.unregisteredClientsDeletionStatus)
|
||||
const blocklistUpdateStatus = useSelector((state) => state.blocklistUpdateStatus)
|
||||
const unregisteredClientsDeletionStatus = useSelector((state) => state.unregisteredClientsDeletionStatus)
|
||||
|
||||
const showSubmitButton = size(unregisteredClients) > 0
|
||||
const hasPendingChanges = null !== blocklistUpdateStatus.pending || size(unregisteredClientsDeletionStatus.pending) > 0
|
||||
|
@ -54,15 +49,14 @@ export default function TroubleshootTab() {
|
|||
}
|
||||
}
|
||||
|
||||
return <>
|
||||
return (
|
||||
<>
|
||||
<div className={sharedStyles['wrapper-div']}>
|
||||
{ hasV3DeprecationWarning && <V3DeprecationWarning /> }
|
||||
<ConflictDetectionScannerSection />
|
||||
<ManageFontAwesomeVersionsSection />
|
||||
<UnregisteredClientsView />
|
||||
</div>
|
||||
{
|
||||
showSubmitButton &&
|
||||
{showSubmitButton && (
|
||||
<div className={classnames(sharedStyles['submit-wrapper'], ['submit'])}>
|
||||
<input
|
||||
type="submit"
|
||||
|
@ -73,36 +67,42 @@ export default function TroubleshootTab() {
|
|||
disabled={!hasPendingChanges}
|
||||
onClick={handleSubmitClick}
|
||||
/>
|
||||
{ hasSubmitted
|
||||
? submitSuccess
|
||||
? <span className={ classnames(sharedStyles['submit-status'], sharedStyles['success']) }>
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={ faCheck } />
|
||||
{hasSubmitted ? (
|
||||
submitSuccess ? (
|
||||
<span className={classnames(sharedStyles['submit-status'], sharedStyles['success'])}>
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faCheck}
|
||||
/>
|
||||
</span>
|
||||
: <div className={ classnames(sharedStyles['submit-status'], sharedStyles['fail']) }>
|
||||
) : (
|
||||
<div className={classnames(sharedStyles['submit-status'], sharedStyles['fail'])}>
|
||||
<div className={classnames(sharedStyles['fail-icon-container'])}>
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={ faSkull } />
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faSkull}
|
||||
/>
|
||||
</div>
|
||||
<div className={sharedStyles['explanation']}>
|
||||
{
|
||||
!!blocklistUpdateStatus.message && <p> { blocklistUpdateStatus.message } </p>
|
||||
}
|
||||
{
|
||||
!!unregisteredClientsDeletionStatus.message && <p> { unregisteredClientsDeletionStatus.message } </p>
|
||||
}
|
||||
{!!blocklistUpdateStatus.message && <p> {blocklistUpdateStatus.message} </p>}
|
||||
{!!unregisteredClientsDeletionStatus.message && <p> {unregisteredClientsDeletionStatus.message} </p>}
|
||||
</div>
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
{
|
||||
isSubmitting
|
||||
? <span className={ classnames(sharedStyles['submit-status'], sharedStyles['submitting']) }>
|
||||
<FontAwesomeIcon className={ sharedStyles['icon'] } icon={faSpinner} spin/>
|
||||
)
|
||||
) : null}
|
||||
{isSubmitting ? (
|
||||
<span className={classnames(sharedStyles['submit-status'], sharedStyles['submitting'])}>
|
||||
<FontAwesomeIcon
|
||||
className={sharedStyles['icon']}
|
||||
icon={faSpinner}
|
||||
spin
|
||||
/>
|
||||
</span>
|
||||
: hasPendingChanges
|
||||
? <span className={ sharedStyles['submit-status'] }>{ __( 'you have pending changes', 'font-awesome' ) }</span>
|
||||
: null
|
||||
}
|
||||
) : hasPendingChanges ? (
|
||||
<span className={sharedStyles['submit-status']}>{__('you have pending changes', 'font-awesome')}</span>
|
||||
) : null}
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
import React from 'react'
|
||||
import { mount } from 'enzyme'
|
||||
import { Provider } from 'react-redux'
|
||||
import { createStore } from './store'
|
||||
import ErrorBoundary from './ErrorBoundary'
|
||||
import TroubleshootTab from './TroubleshootTab'
|
||||
import { unmountComponentAtNode } from 'react-dom'
|
||||
|
||||
describe('TroubleshootTab', () => {
|
||||
let wrapper = null
|
||||
|
||||
beforeEach(() => {
|
||||
const initialData = JSON.parse(
|
||||
'{"apiNonce":"81245cfaf6","apiUrl":"http://localhost:8765/index.php?rest_route=/font-awesome/v1","detectConflictsUntil":"1581103894","unregisteredClients":{"3c937b6d9b50371df1e78b5d70e11512":{"type":"fontawesome-conflict","technology":"webfont","href":"https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.css","innerText":"","tagName":"LINK","blocked":true},"c604f60e1488e3c3493a19a43709b4ca":{"type":"fontawesome-conflict","technology":"webfont","href":"http://localhost:8765/wp-content/plugins/plugin-kappa/font-awesome-4.7.0/css/font-awesome.css","innerText":"","tagName":"LINK","blocked":true}},"showConflictDetectionReporter":"","settingsPageUrl":"http://localhost:8765/wp-admin/options-general.php?page=font-awesome","activeAdminTab":"ADMIN_TAB_TROUBLESHOOT","options":{"usePro":false,"compat":true,"technology":"webfont","pseudoElements":false,"version":"5.12.1"},"showAdmin":"1","onSettingsPage":"1","clientPreferences":{"beta-plugin":{"name":"beta-plugin","compat":true,"version":[["5.1.0",">="]]}},"releases":{"available":["5.12.1","5.12.0","5.11.2","5.11.1","5.11.0","5.10.2","5.10.1","5.10.0","5.9.0","5.8.2","5.8.1","5.8.0","5.7.2","5.7.1","5.7.0","5.6.3","5.6.1","5.6.0","5.5.0","5.4.2","5.4.1","5.3.1","5.2.0","5.1.1","5.1.0","5.0.13","5.0.12","5.0.10","5.0.9","5.0.8","5.0.6","5.0.4","5.0.3","5.0.2","5.0.1"],"latest_version_5":"5.12.1","latest_version_6":"6.1.1"},"pluginVersion":"4.0.0-rc13","preferenceConflicts":[],"v3DeprecationWarning":""}'
|
||||
)
|
||||
|
||||
global['__FontAwesomeOfficialPlugin__'] = initialData
|
||||
|
||||
const store = createStore(initialData)
|
||||
|
||||
wrapper = mount(
|
||||
<ErrorBoundary>
|
||||
<Provider store={ store }>
|
||||
<TroubleshootTab/>
|
||||
</Provider>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.unmount()
|
||||
wrapper = null
|
||||
})
|
||||
|
||||
test('mounts successfully', () => {
|
||||
expect(wrapper).toBeTruthy()
|
||||
})
|
||||
|
||||
describe('when starting with all conflicts blocked', () => {
|
||||
beforeEach(() => {
|
||||
// Assert expectations of the state of the data
|
||||
const inputs = [
|
||||
'block_all_detected_conflicts',
|
||||
'block_3c937b6d9b50371df1e78b5d70e11512',
|
||||
'block_c604f60e1488e3c3493a19a43709b4ca'
|
||||
]
|
||||
|
||||
inputs.forEach(id => {
|
||||
expect(
|
||||
wrapper
|
||||
.find(`#${id}`)
|
||||
.first()
|
||||
.props()
|
||||
.checked
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
expect(
|
||||
wrapper.find('#submit').props().disabled
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
describe('when de-selecting and re-selecting an individual conflict for blocking', () => {
|
||||
test('there are no pending changes shown', () => {
|
||||
// change/click one of the conflicts
|
||||
wrapper.find('#block_c604f60e1488e3c3493a19a43709b4ca').simulate('change')
|
||||
|
||||
// That one should now be unchecked
|
||||
expect(
|
||||
wrapper
|
||||
.find('#block_c604f60e1488e3c3493a19a43709b4ca')
|
||||
.first()
|
||||
.props()
|
||||
.checked
|
||||
).toBe(false)
|
||||
|
||||
// The All select should no longer be checked
|
||||
expect(
|
||||
wrapper
|
||||
.find('#block_all_detected_conflicts')
|
||||
.first()
|
||||
.props()
|
||||
.checked
|
||||
).toBe(false)
|
||||
|
||||
// And there should be pending changes
|
||||
expect(
|
||||
wrapper.find('#submit').props().disabled
|
||||
).toBe(false)
|
||||
|
||||
// Now, just click/change that same one again
|
||||
wrapper.find('#block_c604f60e1488e3c3493a19a43709b4ca').simulate('change')
|
||||
|
||||
// That one should now be checked
|
||||
expect(
|
||||
wrapper
|
||||
.find('#block_c604f60e1488e3c3493a19a43709b4ca')
|
||||
.first()
|
||||
.props()
|
||||
.checked
|
||||
).toBe(true)
|
||||
|
||||
// The All select should be checked again
|
||||
expect(
|
||||
wrapper
|
||||
.find('#block_all_detected_conflicts')
|
||||
.first()
|
||||
.props()
|
||||
.checked
|
||||
).toBe(true)
|
||||
|
||||
// And the submit button should again be disabled, for lack of pending changes
|
||||
expect(
|
||||
wrapper.find('#submit').props().disabled
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,25 +1,14 @@
|
|||
import React from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import {
|
||||
updatePendingBlocklist,
|
||||
updatePendingUnregisteredClientsForDeletion
|
||||
} from './store/actions'
|
||||
import { updatePendingBlocklist, updatePendingUnregisteredClientsForDeletion } from './store/actions'
|
||||
import { blocklistSelector } from './store/reducers'
|
||||
import styles from './UnregisteredClientsView.module.css'
|
||||
import sharedStyles from './App.module.css'
|
||||
import classnames from 'classnames'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import {
|
||||
faCheckSquare,
|
||||
faThumbsUp } from '@fortawesome/free-solid-svg-icons'
|
||||
import {
|
||||
faSquare } from '@fortawesome/free-regular-svg-icons'
|
||||
import get from 'lodash/get'
|
||||
import truncate from 'lodash/truncate'
|
||||
import size from 'lodash/size'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import sortedUnique from 'lodash/sortedUniq'
|
||||
import difference from 'lodash/difference'
|
||||
import { faCheckSquare, faThumbsUp } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faSquare } from '@fortawesome/free-regular-svg-icons'
|
||||
import { get, truncate, size, isEqual, sortedUniq, difference } from 'lodash'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import createInterpolateElement from './createInterpolateElement'
|
||||
|
||||
|
@ -33,93 +22,93 @@ function excerpt( content ) {
|
|||
|
||||
export default function UnregisteredClientsView() {
|
||||
const dispatch = useDispatch()
|
||||
const unregisteredClients = useSelector(state => state.unregisteredClients)
|
||||
const savedBlocklist = useSelector(state => blocklistSelector(state))
|
||||
const blocklist = useSelector(state => {
|
||||
const unregisteredClients = useSelector((state) => state.unregisteredClients)
|
||||
const savedBlocklist = useSelector((state) => blocklistSelector(state))
|
||||
const blocklist = useSelector((state) => {
|
||||
if (null !== state.blocklistUpdateStatus.pending) {
|
||||
return state.blocklistUpdateStatus.pending
|
||||
} else {
|
||||
return savedBlocklist
|
||||
}
|
||||
})
|
||||
const deleteList = useSelector( state => state.unregisteredClientsDeletionStatus.pending)
|
||||
const deleteList = useSelector((state) => state.unregisteredClientsDeletionStatus.pending)
|
||||
const detectedUnregisteredClients = size(Object.keys(unregisteredClients)) > 0
|
||||
const allDetectedConflictsSelectedForBlocking =
|
||||
isEqual(Object.keys(unregisteredClients).sort(), [...(blocklist || [])].sort())
|
||||
const allDetectedConflictsSelectedForRemoval =
|
||||
isEqual(Object.keys(unregisteredClients).sort(), [...(deleteList || [])].sort())
|
||||
const allDetectedConflictsSelectedForBlocking = isEqual(Object.keys(unregisteredClients).sort(), [...(blocklist || [])].sort())
|
||||
const allDetectedConflictsSelectedForRemoval = isEqual(Object.keys(unregisteredClients).sort(), [...(deleteList || [])].sort())
|
||||
const allDetectedConflicts = Object.keys(unregisteredClients)
|
||||
|
||||
function isCheckedForBlocking(md5) {
|
||||
return !! blocklist.find(x => x === md5)
|
||||
return !!blocklist.find((x) => x === md5)
|
||||
}
|
||||
|
||||
function isCheckedForRemoval(md5) {
|
||||
return !! deleteList.find(x => x === md5)
|
||||
return !!deleteList.find((x) => x === md5)
|
||||
}
|
||||
|
||||
function changeCheckForRemoval(md5, allDetectedConflicts) {
|
||||
const newDeleteList = 'all' === md5
|
||||
const newDeleteList =
|
||||
'all' === md5
|
||||
? allDetectedConflictsSelectedForRemoval
|
||||
? [] // uncheck them all
|
||||
: allDetectedConflicts // check them all
|
||||
: isCheckedForRemoval(md5)
|
||||
? deleteList.filter(x => x !== md5)
|
||||
? deleteList.filter((x) => x !== md5)
|
||||
: [...deleteList, md5]
|
||||
|
||||
dispatch(updatePendingUnregisteredClientsForDeletion(newDeleteList))
|
||||
}
|
||||
|
||||
function changeCheckForBlocking(md5, allDetectedConflicts) {
|
||||
const newBlocklist = 'all' === md5
|
||||
const newBlocklist =
|
||||
'all' === md5
|
||||
? allDetectedConflictsSelectedForBlocking
|
||||
? [] // uncheck them all
|
||||
: allDetectedConflicts // check them all
|
||||
: isCheckedForBlocking(md5)
|
||||
? blocklist.filter(x => x !== md5)
|
||||
? blocklist.filter((x) => x !== md5)
|
||||
: [...blocklist, md5]
|
||||
|
||||
const orig = sortedUnique( savedBlocklist )
|
||||
const updated = sortedUnique( newBlocklist )
|
||||
const orig = sortedUniq(savedBlocklist)
|
||||
const updated = sortedUniq(newBlocklist)
|
||||
|
||||
if(
|
||||
orig.length === updated.length &&
|
||||
0 === size( difference(orig, updated) ) &&
|
||||
0 === size( difference(updated, orig) )
|
||||
) {
|
||||
if (orig.length === updated.length && 0 === size(difference(orig, updated)) && 0 === size(difference(updated, orig))) {
|
||||
dispatch(updatePendingBlocklist(null))
|
||||
} else {
|
||||
dispatch(updatePendingBlocklist(newBlocklist))
|
||||
}
|
||||
}
|
||||
|
||||
return <div className={ classnames(styles['unregistered-clients'], { [styles['none-detected']]: !detectedUnregisteredClients }) }>
|
||||
return (
|
||||
<div className={classnames(styles['unregistered-clients'], { [styles['none-detected']]: !detectedUnregisteredClients })}>
|
||||
<h3 className={sharedStyles['section-title']}>{__('Other themes or plugins', 'font-awesome')}</h3>
|
||||
{detectedUnregisteredClients
|
||||
? <div>
|
||||
{detectedUnregisteredClients ? (
|
||||
<div>
|
||||
<p className={sharedStyles['explanation']}>
|
||||
{
|
||||
__( 'Below is the list of other versions of Font Awesome from active plugins or themes that are loading on your site. Check off any that you would like to block from loading. Normally this just blocks the conflicting version of Font Awesome and doesn\'t affect the other functions of the plugin, but you should verify your site works as expected. If you think you\'ve fixed a found conflict, you can clear it from the table.', 'font-awesome' )
|
||||
}
|
||||
{__(
|
||||
"Below is the list of other versions of Font Awesome from active plugins or themes that are loading on your site. Check off any that you would like to block from loading. Normally this just blocks the conflicting version of Font Awesome and doesn't affect the other functions of the plugin, but you should verify your site works as expected. If you think you've fixed a found conflict, you can clear it from the table.",
|
||||
'font-awesome'
|
||||
)}
|
||||
</p>
|
||||
<table className={classnames('widefat', 'striped')}>
|
||||
<thead>
|
||||
<tr className={sharedStyles['table-header']}>
|
||||
<th>
|
||||
<div className={styles['column-label']}>{__('Block', 'font-awesome')}</div>
|
||||
{
|
||||
size( allDetectedConflicts ) > 1 &&
|
||||
{size(allDetectedConflicts) > 1 && (
|
||||
<div className={styles['block-all-container']}>
|
||||
<input
|
||||
id='block_all_detected_conflicts'
|
||||
name='block_all_detected_conflicts'
|
||||
id="block_all_detected_conflicts"
|
||||
name="block_all_detected_conflicts"
|
||||
type="checkbox"
|
||||
value='all'
|
||||
value="all"
|
||||
checked={allDetectedConflictsSelectedForBlocking}
|
||||
onChange={() => changeCheckForBlocking('all', allDetectedConflicts)}
|
||||
className={classnames(sharedStyles['sr-only'], sharedStyles['input-checkbox-custom'])}
|
||||
/>
|
||||
<label htmlFor='block_all_detected_conflicts' className={ styles['checkbox-label'] }>
|
||||
<label
|
||||
htmlFor="block_all_detected_conflicts"
|
||||
className={styles['checkbox-label']}
|
||||
>
|
||||
<span className={sharedStyles['relative']}>
|
||||
<FontAwesomeIcon
|
||||
icon={faCheckSquare}
|
||||
|
@ -137,33 +126,31 @@ export default function UnregisteredClientsView() {
|
|||
{__('All', 'font-awesome')}
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</th>
|
||||
<th>
|
||||
<span className={ styles['column-label'] }>
|
||||
{ __( 'Type', 'font-awesome' ) }
|
||||
</span>
|
||||
<span className={styles['column-label']}>{__('Type', 'font-awesome')}</span>
|
||||
</th>
|
||||
<th>
|
||||
<span className={ styles['column-label'] }>
|
||||
{ __( 'URL', 'font-awesome' ) }
|
||||
</span>
|
||||
<span className={styles['column-label']}>{__('URL', 'font-awesome')}</span>
|
||||
</th>
|
||||
<th>
|
||||
<div className={styles['column-label']}>{__('Clear', 'font-awesome')}</div>
|
||||
{
|
||||
size( allDetectedConflicts ) > 1 &&
|
||||
{size(allDetectedConflicts) > 1 && (
|
||||
<div className={styles['remove-all-container']}>
|
||||
<input
|
||||
id='remove_all_detected_conflicts'
|
||||
name='remove_all_detected_conflicts'
|
||||
id="remove_all_detected_conflicts"
|
||||
name="remove_all_detected_conflicts"
|
||||
type="checkbox"
|
||||
value='all'
|
||||
value="all"
|
||||
checked={allDetectedConflictsSelectedForRemoval}
|
||||
onChange={() => changeCheckForRemoval('all', allDetectedConflicts)}
|
||||
className={classnames(sharedStyles['sr-only'], sharedStyles['input-checkbox-custom'])}
|
||||
/>
|
||||
<label htmlFor='remove_all_detected_conflicts' className={ styles['checkbox-label'] }>
|
||||
<label
|
||||
htmlFor="remove_all_detected_conflicts"
|
||||
className={styles['checkbox-label']}
|
||||
>
|
||||
<span className={sharedStyles['relative']}>
|
||||
<FontAwesomeIcon
|
||||
icon={faCheckSquare}
|
||||
|
@ -181,13 +168,12 @@ export default function UnregisteredClientsView() {
|
|||
{__('All', 'font-awesome')}
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
allDetectedConflicts.map(md5 => (
|
||||
{allDetectedConflicts.map((md5) => (
|
||||
<tr key={md5}>
|
||||
<td>
|
||||
<input
|
||||
|
@ -199,7 +185,10 @@ export default function UnregisteredClientsView() {
|
|||
onChange={() => changeCheckForBlocking(md5)}
|
||||
className={classnames(sharedStyles['sr-only'], sharedStyles['input-checkbox-custom'])}
|
||||
/>
|
||||
<label htmlFor={`block_${md5}`} className={ styles['checkbox-label'] }>
|
||||
<label
|
||||
htmlFor={`block_${md5}`}
|
||||
className={styles['checkbox-label']}
|
||||
>
|
||||
<span className={sharedStyles['relative']}>
|
||||
<FontAwesomeIcon
|
||||
icon={faCheckSquare}
|
||||
|
@ -216,27 +205,21 @@ export default function UnregisteredClientsView() {
|
|||
</span>
|
||||
</label>
|
||||
</td>
|
||||
<td>{get(unregisteredClients[md5], 'tagName', 'unknown').toLowerCase()}</td>
|
||||
<td>
|
||||
{get(unregisteredClients[md5], 'tagName', 'unknown').toLowerCase()}
|
||||
</td>
|
||||
<td>
|
||||
{
|
||||
unregisteredClients[md5].src
|
||||
|| unregisteredClients[md5].href
|
||||
|| createInterpolateElement(
|
||||
__( '<em>in page source. </em><excerpt/>', 'font-awesome' ),
|
||||
{
|
||||
{unregisteredClients[md5].src ||
|
||||
unregisteredClients[md5].href ||
|
||||
createInterpolateElement(__('<em>in page source. </em><excerpt/>', 'font-awesome'), {
|
||||
em: <em />,
|
||||
excerpt: (
|
||||
( content ) => content
|
||||
? <>
|
||||
excerpt: ((content) =>
|
||||
content ? (
|
||||
<>
|
||||
File starts with: <code>{content}</code>
|
||||
</>
|
||||
: ''
|
||||
) ( excerpt( get(unregisteredClients[md5], 'innerText') ) )
|
||||
}
|
||||
)
|
||||
}
|
||||
) : (
|
||||
''
|
||||
))(excerpt(get(unregisteredClients[md5], 'innerText')))
|
||||
})}
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
|
@ -248,7 +231,10 @@ export default function UnregisteredClientsView() {
|
|||
onChange={() => changeCheckForRemoval(md5)}
|
||||
className={classnames(sharedStyles['sr-only'], sharedStyles['input-checkbox-custom'])}
|
||||
/>
|
||||
<label htmlFor={`remove_${md5}`} className={ styles['checkbox-label'] }>
|
||||
<label
|
||||
htmlFor={`remove_${md5}`}
|
||||
className={styles['checkbox-label']}
|
||||
>
|
||||
<span className={sharedStyles['relative']}>
|
||||
<FontAwesomeIcon
|
||||
icon={faCheckSquare}
|
||||
|
@ -266,19 +252,21 @@ export default function UnregisteredClientsView() {
|
|||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
: <div className={ classnames(sharedStyles['explanation'], sharedStyles['flex'], sharedStyles['flex-row'] )}>
|
||||
) : (
|
||||
<div className={classnames(sharedStyles['explanation'], sharedStyles['flex'], sharedStyles['flex-row'])}>
|
||||
<div>
|
||||
<FontAwesomeIcon icon={ faThumbsUp } size='lg'/>
|
||||
<FontAwesomeIcon
|
||||
icon={faThumbsUp}
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
<div className={ sharedStyles['space-left'] }>
|
||||
{ __( 'We haven\'t detected any plugins or themes trying to load Font Awesome.', 'font-awesome' ) }
|
||||
<div className={sharedStyles['space-left']}>{__("We haven't detected any plugins or themes trying to load Font Awesome.", 'font-awesome')}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
import React from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faClock, faSpinner, faCheck, faSkull } from '@fortawesome/free-solid-svg-icons'
|
||||
import { snoozeV3DeprecationWarning } from './store/actions'
|
||||
import styles from './V3DeprecationWarning.module.css'
|
||||
import classnames from 'classnames'
|
||||
import Alert from './Alert'
|
||||
import { __, sprintf } from '@wordpress/i18n'
|
||||
import createInterpolateElement from './createInterpolateElement'
|
||||
|
||||
export default function V3DeprecationWarning() {
|
||||
const { snooze, atts, v5name, v5prefix } = useSelector(state => state.v3DeprecationWarning)
|
||||
const { isSubmitting, hasSubmitted, success } = useSelector(state => state.v3DeprecationWarningStatus)
|
||||
const dispatch = useDispatch()
|
||||
|
||||
if (snooze) return null
|
||||
|
||||
return <Alert
|
||||
title={ __( 'Font Awesome 3 icon names are deprecated', 'font-awesome' ) }
|
||||
type='warning'
|
||||
>
|
||||
<p>
|
||||
{
|
||||
createInterpolateElement(
|
||||
sprintf(
|
||||
__('Looks like you\'re using an old Font Awesome 3 icon name in your shortcode: <code>%s</code>. We discontinued support for Font Awesome 3 quite some time ago. Won\'t you jump into <a>the newest Font Awesome</a> with us? It\'s way better, and it\'s easy to upgrade.', 'font-awesome' ),
|
||||
atts.name
|
||||
),
|
||||
{
|
||||
code: <code />,
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
a: <a rel="noopener noreferrer" target="_blank" href="https://fontawesome.com/" />
|
||||
}
|
||||
)
|
||||
}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{ __('Just adjust your shortcode from this:', 'font-awesome' ) }
|
||||
</p>
|
||||
|
||||
<blockquote><code>[icon name="{ atts.name }"]</code></blockquote>
|
||||
|
||||
<p>
|
||||
{ __( 'to this:', 'font-awesome' ) }
|
||||
</p>
|
||||
|
||||
<blockquote><code>[icon name="{ v5name }" prefix="{ v5prefix }"]</code></blockquote>
|
||||
|
||||
<p>
|
||||
{
|
||||
createInterpolateElement(
|
||||
__( 'You\'ll need to go adjust any version 3 icon names in [icon] shortcodes in your pages, posts, widgets, templates (or wherever they\'re coming from) to the new format with prefix. You can check the icon names and prefixes in our <linkIconGallery>Icon Gallery</linkIconGallery>. But what\'s that prefix, you ask? We now support a number of different styles for each icon. <linkLearnMore>Learn more</linkLearnMore>', 'font-awesome' ),
|
||||
{
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
linkIconGallery: <a rel="noopener noreferrer" target="_blank" href="https://fontawesome.com/icons?d=gallery" />,
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
linkLearnMore: <a rel="noopener noreferrer" target="_blank" href="https://fontawesome.com/how-to-use/on-the-web/setup/upgrading-from-version-4#changes" />
|
||||
}
|
||||
)
|
||||
}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{
|
||||
createInterpolateElement(
|
||||
__( 'Once you update your icon shortcodes, this warning will disappear or you could hit snooze to hide it for a while. <strong>But we\'re gonna remove this v3-to-v5 magic soon, though, so don\'t wait forever.</strong>', 'font-awesome' ),
|
||||
{
|
||||
strong: <strong />
|
||||
}
|
||||
)
|
||||
}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<button disabled={ isSubmitting } onClick={ () => dispatch(snoozeV3DeprecationWarning()) } className={ classnames( styles['snooze-button'], 'button', 'button-primary' ) }>
|
||||
{
|
||||
isSubmitting
|
||||
? <FontAwesomeIcon icon={ faSpinner } spin className={ styles['submitting'] } />
|
||||
: hasSubmitted
|
||||
? success
|
||||
? <FontAwesomeIcon icon={ faCheck } className={ styles['success'] }/>
|
||||
: <FontAwesomeIcon icon={ faSkull } className={ styles['fail'] }/>
|
||||
: <FontAwesomeIcon icon={ faClock } className={ styles['snooze'] }/>
|
||||
}
|
||||
<span className={ styles['label'] }>{ __( 'Snooze', 'font-awesome' ) }</span>
|
||||
</button>
|
||||
</p>
|
||||
</Alert>
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
.v3-deprecation-warning {
|
||||
border: 1px solid black;
|
||||
background-color: #fdfdf3;
|
||||
padding: 1.5em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.snooze-button {
|
||||
padding: .5rem;
|
||||
background-color: rgba(0,0,0,0);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.snooze-button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.snooze-button .label {
|
||||
margin-left: 1em;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import get from 'lodash/get'
|
||||
import set from 'lodash/set'
|
||||
const get = require('lodash/get')
|
||||
const set = require('lodash/set')
|
||||
|
||||
// NOTE: the Jest docs on manual mocks indicate that mocks for things under
|
||||
// node_modules should be in a __mocks__ directory that is adjacent to node_modules.
|
||||
|
@ -9,7 +9,7 @@ import set from 'lodash/set'
|
|||
// the root for Jest in such a way that __mocks__ as to live under the src directory.
|
||||
// See: https://github.com/facebook/create-react-app/issues/7539#issuecomment-531463603
|
||||
|
||||
const DEFAULT_INTERCEPTOR = thing => thing
|
||||
const DEFAULT_INTERCEPTOR = (thing) => thing
|
||||
const DEFAULT_PUT = (url, _data, _config) => handleRequest({ url, method: 'PUT' })
|
||||
const DEFAULT_POST = (url, _data, _config) => handleRequest({ url, method: 'POST' })
|
||||
const DEFAULT_DELETE = (url, _data, _config) => handleRequest({ url, method: 'DELETE' })
|
||||
|
@ -33,13 +33,15 @@ const axios = {
|
|||
|
||||
axios.create = () => axios
|
||||
|
||||
export function respondWith ({ url, method = "GET", response }) {
|
||||
export function respondWith({ url, method = 'GET', response }) {
|
||||
responses = set(responses, [url, method.toUpperCase()], response)
|
||||
}
|
||||
|
||||
export function resetAxiosMocks() {
|
||||
responses = {}
|
||||
axios.put = DEFAULT_PUT
|
||||
axios.post = DEFAULT_POST
|
||||
axios.delete = DEFAULT_DELETE
|
||||
}
|
||||
|
||||
export function changeImpl({ name, fn }) {
|
||||
|
|
|
@ -1,131 +0,0 @@
|
|||
import React, { useState } from 'react'
|
||||
import { Modal } from '@wordpress/components'
|
||||
import { FaIconChooser } from '@fortawesome/fa-icon-chooser-react'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import createInterpolateElement from '../createInterpolateElement'
|
||||
|
||||
const IconChooserModal = (props) => {
|
||||
const { onSubmit, kitToken, version, pro, handleQuery, modalOpenEvent, getUrlText, settingsPageUrl } = props
|
||||
const [ isOpen, setOpen ] = useState( false )
|
||||
|
||||
document.addEventListener(modalOpenEvent.type, () => setOpen(true))
|
||||
|
||||
const closeModal = () => setOpen( false )
|
||||
|
||||
const submitAndCloseModal = (result) => {
|
||||
if('function' === typeof onSubmit) {
|
||||
onSubmit(result)
|
||||
}
|
||||
closeModal()
|
||||
}
|
||||
|
||||
const isProCdn = !!pro && !kitToken
|
||||
|
||||
return (
|
||||
<>
|
||||
{ isOpen && (
|
||||
<Modal title="Add a Font Awesome Icon" onRequestClose={ closeModal }>
|
||||
{
|
||||
isProCdn &&
|
||||
<div style={{ margin: '1em', backgroundColor: '#FFD200', padding: '1em', borderRadius: '.5em', fontSize: '15px'}}>
|
||||
{__( 'Looking for Pro icons and styles? You’ll need to use a kit. ', 'font-awesome' ) }
|
||||
|
||||
<a href={settingsPageUrl}>{__('Go to Font Awesome Plugin Settings', 'font-awesome')}</a>
|
||||
</div>
|
||||
}
|
||||
<FaIconChooser
|
||||
version={ version }
|
||||
kitToken={ kitToken }
|
||||
handleQuery={ handleQuery }
|
||||
getUrlText={ getUrlText }
|
||||
onFinish={ result => submitAndCloseModal(result) }
|
||||
searchInputPlaceholder={__('Find icons by name, category, or keyword', 'font-awesome')}
|
||||
>
|
||||
<span slot='fatal-error-heading'>
|
||||
{ __('Well, this is awkward...', 'font-awesome') }
|
||||
</span>
|
||||
<span slot='fatal-error-detail'>
|
||||
{ __('Something has gone horribly wrong. Check the console for additional error information.', 'font-awesome') }
|
||||
</span>
|
||||
<span slot="start-view-heading">
|
||||
{__( "Font Awesome is the web's most popular icon set, with tons of icons in a variety of styles.", 'font-awesome' ) }
|
||||
</span>
|
||||
<span slot="start-view-detail">
|
||||
{
|
||||
createInterpolateElement(
|
||||
__( "Not sure where to start? Here are some favorites, or try a search for <strong>spinners</strong>, <strong>shopping</strong>, <strong>food</strong>, or <strong>whatever you're looking for</strong>.", 'font-awesome'),
|
||||
{
|
||||
strong: <strong/>
|
||||
}
|
||||
)
|
||||
}
|
||||
</span>
|
||||
<span slot='search-field-label-free'>
|
||||
{__('Search Font Awesome Free Icons in Version', 'font-awesome')}
|
||||
</span>
|
||||
<span slot='search-field-label-pro'>
|
||||
{__('Search Font Awesome Pro Icons in Version', 'font-awesome')}
|
||||
</span>
|
||||
<span slot='searching-free'>
|
||||
{__("You're searching Font Awesome Free icons in version", 'font-awesome')}
|
||||
</span>
|
||||
<span slot='searching-pro'>
|
||||
{__("You're searching Font Awesome Pro icons in version", 'font-awesome')}
|
||||
</span>
|
||||
<span slot='light-requires-pro'>
|
||||
{__('You need to use a Pro kit to get Light icons.', 'font-awesome')}
|
||||
</span>
|
||||
<span slot='thin-requires-pro'>
|
||||
{__('You need to use a Pro kit with Version 6 to get Thin icons.', 'font-awesome')}
|
||||
</span>
|
||||
<span slot='duotone-requires-pro'>
|
||||
{__('You need to use a Pro kit with Version 5.10 or later to get Duotone icons.', 'font-awesome')}
|
||||
</span>
|
||||
<span slot='uploaded-requires-pro'>
|
||||
{__('You need to use a Pro kit to get Uploaded icons.', 'font-awesome')}
|
||||
</span>
|
||||
<span slot='kit-has-no-uploaded-icons'>
|
||||
{__('This kit contains no uploaded icons.', 'font-awesome')}
|
||||
</span>
|
||||
<span slot='no-search-results-heading'>
|
||||
{__("Sorry, we couldn't find anything for that.", 'font-awesome')}
|
||||
</span>
|
||||
<span slot='no-search-results-detail'>
|
||||
{__('You might try a different search...', 'font-awesome')}
|
||||
</span>
|
||||
<span slot="suggest-icon-upload">
|
||||
{
|
||||
createInterpolateElement(
|
||||
__( 'Or <a>upload your own icon</a> to a Pro kit!', 'font-awesome'),
|
||||
{
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
a: <a target="_blank" rel="noopener noreferrer" href="https://fontawesome.com/v5.15/how-to-use/on-the-web/using-kits/uploading-icons" />
|
||||
}
|
||||
)
|
||||
}
|
||||
</span>
|
||||
<span slot='get-fontawesome-pro'>
|
||||
{
|
||||
createInterpolateElement(
|
||||
__( 'Or <a>use Font Awesome Pro</a> for more icons and styles!', 'font-awesome'),
|
||||
{
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
a: <a target="_blank" rel="noopener noreferrer" href="https://fontawesome.com/" />
|
||||
}
|
||||
)
|
||||
}
|
||||
</span>
|
||||
<span slot='initial-loading-view-heading'>
|
||||
{__('Fetching icons', 'font-awesome') }
|
||||
</span>
|
||||
<span slot='initial-loading-view-detail'>
|
||||
{__('When this thing gets up to 88 mph...', 'font-awesome')}
|
||||
</span>
|
||||
</FaIconChooser>
|
||||
</Modal>
|
||||
) }
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconChooserModal
|
|
@ -1,87 +0,0 @@
|
|||
import IconChooserModal from './IconChooserModal'
|
||||
import { buildShortCodeFromIconChooserResult } from './shortcode'
|
||||
import { Fragment, Component } from '@wordpress/element'
|
||||
import { SVG, Path } from '@wordpress/components'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import { insert, registerFormatType } from '@wordpress/rich-text'
|
||||
import { RichTextToolbarButton } from '@wordpress/block-editor'
|
||||
|
||||
export function setupBlockEditor (params) {
|
||||
// TODO: is this the right block type name for what we're doing here?
|
||||
const name = 'font-awesome/icon'
|
||||
const title = __('Font Awesome Icon')
|
||||
|
||||
const {
|
||||
modalOpenEvent,
|
||||
kitToken,
|
||||
version,
|
||||
pro,
|
||||
handleQuery,
|
||||
getUrlText,
|
||||
settingsPageUrl
|
||||
} = params
|
||||
|
||||
registerFormatType(name, {
|
||||
name,
|
||||
title: __( 'Font Awesome Icon' ),
|
||||
keywords: [ __( 'icon' ), __( 'font awesome' ) ],
|
||||
tagName: 'i',
|
||||
className: null,
|
||||
// if object is true, then the HTML rendered for this type will lack a closing tag.
|
||||
object: false,
|
||||
edit: class FontAwesomeIconEdit extends Component {
|
||||
constructor(props) {
|
||||
super( ...arguments )
|
||||
|
||||
this.handleFormatButtonClick = this.handleFormatButtonClick.bind(this)
|
||||
this.handleSelect = this.handleSelect.bind(this)
|
||||
}
|
||||
|
||||
handleFormatButtonClick() {
|
||||
document.dispatchEvent(modalOpenEvent)
|
||||
}
|
||||
|
||||
handleSelect(event){
|
||||
const { value, onChange } = this.props
|
||||
// TODO: this would indicate an invalid event. Do we want some error handling here?
|
||||
if(!event.detail) return
|
||||
|
||||
const shortcode = buildShortCodeFromIconChooserResult(event.detail)
|
||||
onChange( insert( value, shortcode ) )
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Fragment>
|
||||
<RichTextToolbarButton
|
||||
icon={
|
||||
<SVG
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512"
|
||||
className="svg-inline--fa fa-font-awesome fa-w-14">
|
||||
<Path
|
||||
fill="currentColor"
|
||||
d="M397.8 32H50.2C22.7 32 0 54.7 0 82.2v347.6C0 457.3 22.7 480 50.2 480h347.6c27.5 0 50.2-22.7 50.2-50.2V82.2c0-27.5-22.7-50.2-50.2-50.2zm-45.4 284.3c0 4.2-3.6 6-7.8 7.8-16.7 7.2-34.6 13.7-53.8 13.7-26.9 0-39.4-16.7-71.7-16.7-23.3 0-47.8 8.4-67.5 17.3-1.2.6-2.4.6-3.6 1.2V385c0 1.8 0 3.6-.6 4.8v1.2c-2.4 8.4-10.2 14.3-19.1 14.3-11.3 0-20.3-9-20.3-20.3V166.4c-7.8-6-13.1-15.5-13.1-26.3 0-18.5 14.9-33.5 33.5-33.5 18.5 0 33.5 14.9 33.5 33.5 0 10.8-4.8 20.3-13.1 26.3v18.5c1.8-.6 3.6-1.2 5.4-2.4 18.5-7.8 40.6-14.3 61.5-14.3 22.7 0 40.6 6 60.9 13.7 4.2 1.8 8.4 2.4 13.1 2.4 22.7 0 47.8-16.1 53.8-16.1 4.8 0 9 3.6 9 7.8v140.3z"
|
||||
/>
|
||||
</SVG>
|
||||
}
|
||||
title={ title }
|
||||
onClick={ this.handleFormatButtonClick }
|
||||
/>
|
||||
<IconChooserModal
|
||||
modalOpenEvent={ modalOpenEvent }
|
||||
kitToken={ kitToken }
|
||||
version={ version }
|
||||
pro={ pro }
|
||||
settingsPageUrl={ settingsPageUrl }
|
||||
handleQuery={ handleQuery }
|
||||
onSubmit={ this.handleSelect }
|
||||
getUrlText={ getUrlText }
|
||||
/>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import IconChooserModal from './IconChooserModal'
|
||||
import { buildShortCodeFromIconChooserResult } from './shortcode'
|
||||
import get from 'lodash/get'
|
||||
|
||||
export function handleSubmit(event) {
|
||||
const insert = get(window, 'wp.media.editor.insert')
|
||||
insert && insert( buildShortCodeFromIconChooserResult(event.detail) )
|
||||
}
|
||||
|
||||
export function setupClassicEditor(params) {
|
||||
const {
|
||||
iconChooserContainerId,
|
||||
modalOpenEvent,
|
||||
kitToken,
|
||||
version,
|
||||
pro,
|
||||
handleQuery,
|
||||
getUrlText,
|
||||
settingsPageUrl
|
||||
} = params
|
||||
|
||||
const container = document.querySelector(`#${iconChooserContainerId}`)
|
||||
if(!container) return
|
||||
if(!window.tinymce) return
|
||||
|
||||
let wpComponentsStyleAdded = false
|
||||
|
||||
if(!wpComponentsStyleAdded) {
|
||||
wpComponentsStyleAdded = true
|
||||
|
||||
import('@wordpress/components/build-style/style.css')
|
||||
.then(() => {})
|
||||
.catch(err =>
|
||||
console.error(
|
||||
'Font Awesome Plugin failed to load styles for the Icon Chooser in the Classic Editor',
|
||||
err
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: consider how to add Font Awesome to the Tiny MCE visual pane.
|
||||
// But there maybe unexpected behaviors.
|
||||
/*
|
||||
const editor = tinymce.activeEditor
|
||||
|
||||
editor.on('init', e => {
|
||||
const script = editor.dom.doc.createElement('script')
|
||||
script.setAttribute('src', 'https://kit.fontawesome.com/fakekit.js')
|
||||
script.setAttribute('crossorigin', 'anonymous')
|
||||
editor.dom.doc.head.appendChild(script)
|
||||
})
|
||||
*/
|
||||
|
||||
ReactDOM.render(
|
||||
<IconChooserModal
|
||||
kitToken={ kitToken }
|
||||
version={ version }
|
||||
pro={ pro }
|
||||
modalOpenEvent={ modalOpenEvent }
|
||||
handleQuery={ handleQuery }
|
||||
settingsPageUrl={ settingsPageUrl }
|
||||
onSubmit={ handleSubmit }
|
||||
getUrlText={ getUrlText }
|
||||
/>,
|
||||
container
|
||||
)
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
import apiFetch from '@wordpress/api-fetch'
|
||||
|
||||
const configureQueryHandler = params => async (query) => {
|
||||
try {
|
||||
const { apiNonce, rootUrl, restApiNamespace } = params
|
||||
|
||||
// If apiFetch is from wp.apiFetch, it may already have RootURLMiddleware set up.
|
||||
// If we're using the fallback (i.e. when running in the Classic Editor), then
|
||||
// it doesn't yet have thr RootURLMiddleware.
|
||||
// We want to guarantee that it's there, so we'll always add it.
|
||||
// So what if it was already there? Experiment seems to have shown that this
|
||||
// is idempotent. It doesn't seem to hurt to just do it again, so we will.
|
||||
apiFetch.use( apiFetch.createRootURLMiddleware( rootUrl ) )
|
||||
|
||||
// We need the nonce to be set up because we're going to run our query through
|
||||
// the API controller end point, which requires non-public authorization.
|
||||
apiFetch.use( apiFetch.createNonceMiddleware( apiNonce ) )
|
||||
|
||||
return await apiFetch( {
|
||||
path: `${restApiNamespace}/api`,
|
||||
method: 'POST',
|
||||
body: query
|
||||
} )
|
||||
} catch( error ) {
|
||||
console.error('CAUGHT:', error)
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default configureQueryHandler
|
|
@ -1,51 +0,0 @@
|
|||
import get from 'lodash/get'
|
||||
import { setupBlockEditor } from './blockEditor'
|
||||
import { setupClassicEditor } from './classicEditor'
|
||||
|
||||
let classicEditorSetupComplete = false
|
||||
|
||||
function setupClassicEditorIconChooser(initialParams) {
|
||||
// We only want to do this once.
|
||||
if(classicEditorSetupComplete) return
|
||||
if(!window.tinymce) return
|
||||
|
||||
const params = {
|
||||
...initialParams,
|
||||
iconChooserContainerId: 'font-awesome-icon-chooser-container',
|
||||
iconChooserMediaButtonClass: 'font-awesome-icon-chooser-media-button'
|
||||
}
|
||||
|
||||
setupClassicEditor(params)
|
||||
|
||||
classicEditorSetupComplete = true
|
||||
}
|
||||
|
||||
export function setupIconChooser(initialParams) {
|
||||
const params = {
|
||||
...initialParams,
|
||||
modalOpenEvent: new Event('fontAwesomeIconChooserOpen', { "bubbles": true, "cancelable": false })
|
||||
}
|
||||
|
||||
window['__FontAwesomeOfficialPlugin__openIconChooserModal'] = () => {
|
||||
document.dispatchEvent(params.modalOpenEvent)
|
||||
}
|
||||
|
||||
if( !! get(initialParams, 'isGutenbergPage') ) {
|
||||
setupBlockEditor(params)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tiny MCE loading time: In WordPress 5, it's straightforward to enqueue
|
||||
* this script with a script dependency of wp-tinymce. But that's not available
|
||||
* in WP 4, and there doesn't seem to be any way to ensure that the Tiny MCE
|
||||
* script has been loaded before this, other than to add a script after the
|
||||
* Tiny MCE scripts have been printed.
|
||||
*
|
||||
* So what we'll do instead is simply export this function that can be exposed
|
||||
* as a global function, and in our back end PHP code, we'll add an inline script
|
||||
* to invoke that global for tinyMCE setup if and when it is necessary.
|
||||
*/
|
||||
return {
|
||||
setupClassicEditorIconChooser: () => setupClassicEditorIconChooser(params)
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export const GLOBAL_KEY = '__FontAwesomeOfficialPlugin__'
|
|
@ -1,8 +1,11 @@
|
|||
import { createStore } from './store'
|
||||
import get from 'lodash/get'
|
||||
import { get, set } from 'lodash'
|
||||
import { GLOBAL_KEY } from './constants'
|
||||
import createInterpolateElement from './createInterpolateElement'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
|
||||
const initialData = window['__FontAwesomeOfficialPlugin__']
|
||||
__webpack_public_path__ = get(initialData, 'webpackPublicPath')
|
||||
const initialData = window[GLOBAL_KEY]
|
||||
// See: https://webpack.js.org/guides/public-path/#on-the-fly
|
||||
const CONFLICT_DETECTION_REPORT_EVENT_TYPE = 'fontAwesomeConflictDetectionReport'
|
||||
/**
|
||||
* This will start out as falsy, when there's a report, we'll set it with those
|
||||
|
@ -15,7 +18,7 @@ const CONFLICT_DETECTION_REPORT_EVENT_TYPE = 'fontAwesomeConflictDetectionReport
|
|||
let conflictDetectionReport = null
|
||||
|
||||
if (get(initialData, 'showConflictDetectionReporter')) {
|
||||
const reportEvent = new Event(CONFLICT_DETECTION_REPORT_EVENT_TYPE, { "bubbles": true, "cancelable": false })
|
||||
const reportEvent = new Event(CONFLICT_DETECTION_REPORT_EVENT_TYPE, { bubbles: true, cancelable: false })
|
||||
|
||||
/**
|
||||
* If we're doing conflict detection, we must set this up before DOMContentLoaded,
|
||||
|
@ -23,66 +26,37 @@ if( get(initialData, 'showConflictDetectionReporter') ) {
|
|||
*/
|
||||
window.FontAwesomeDetection = {
|
||||
...(window.FontAwesomeDetection || {}),
|
||||
report: params => {
|
||||
report: (params) => {
|
||||
conflictDetectionReport = params
|
||||
document.dispatchEvent(reportEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* First, we need to resolve whether we're using external dependencies available
|
||||
* in WordPress 5 core, or whether we've loaded our WordPress 4 compatibility
|
||||
* bundle. Regardless, the webpack config will use this global for settting up
|
||||
* externals.
|
||||
*/
|
||||
if( !window.__Font_Awesome_Webpack_Externals__ ) {
|
||||
window.__Font_Awesome_Webpack_Externals__ = {
|
||||
React: get(window, 'React'),
|
||||
ReactDOM: get(window, 'ReactDOM'),
|
||||
i18n: get(window, 'wp.i18n'),
|
||||
apiFetch: get(window, 'wp.apiFetch'),
|
||||
components: get(window, 'wp.components'),
|
||||
element: get(window, 'wp.element'),
|
||||
richText: get(window, 'wp.richText'),
|
||||
blockEditor: get(window, 'wp.blockEditor'),
|
||||
domReady: get(window, 'wp.domReady')
|
||||
}
|
||||
}
|
||||
|
||||
const { __ } = __Font_Awesome_Webpack_Externals__.i18n
|
||||
|
||||
if (!initialData) {
|
||||
console.error(__('Font Awesome plugin is broken: initial state data missing.', 'font-awesome'))
|
||||
}
|
||||
|
||||
const store = createStore(initialData)
|
||||
|
||||
const {
|
||||
showAdmin,
|
||||
showConflictDetectionReporter,
|
||||
enableIconChooser,
|
||||
usingCompatJs,
|
||||
isGutenbergPage
|
||||
} = store.getState()
|
||||
set(window, [GLOBAL_KEY, 'createInterpolateElement'], createInterpolateElement)
|
||||
|
||||
const { showAdmin, showConflictDetectionReporter } = store.getState()
|
||||
|
||||
if (showAdmin) {
|
||||
import('./mountAdminView')
|
||||
.then(({ default: mountAdminView }) => {
|
||||
mountAdminView(store)
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
console.error(__('Font Awesome plugin error when initializing admin settings view', 'font-awesome'), error)
|
||||
})
|
||||
}
|
||||
|
||||
if (showConflictDetectionReporter) {
|
||||
Promise.all([
|
||||
import('./store/actions'),
|
||||
import('./mountConflictDetectionReporter')
|
||||
])
|
||||
Promise.all([import('./store/actions'), import('./mountConflictDetectionReporter')])
|
||||
.then(([{ reportDetectedConflicts }, { mountConflictDetectionReporter }]) => {
|
||||
const report = params => store.dispatch(reportDetectedConflicts(params))
|
||||
const report = (params) => store.dispatch(reportDetectedConflicts(params))
|
||||
|
||||
/**
|
||||
* If the conflict detection report is already available, just use it;
|
||||
|
@ -91,59 +65,12 @@ if( showConflictDetectionReporter ) {
|
|||
if (conflictDetectionReport) {
|
||||
report(conflictDetectionReport)
|
||||
} else {
|
||||
document.addEventListener(
|
||||
CONFLICT_DETECTION_REPORT_EVENT_TYPE,
|
||||
_event => report(conflictDetectionReport)
|
||||
)
|
||||
document.addEventListener(CONFLICT_DETECTION_REPORT_EVENT_TYPE, (_event) => report(conflictDetectionReport))
|
||||
}
|
||||
|
||||
mountConflictDetectionReporter(store)
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
console.error(__('Font Awesome plugin error when initializing conflict detection scanner', 'font-awesome'), error)
|
||||
})
|
||||
}
|
||||
|
||||
if ( enableIconChooser ) {
|
||||
if ( usingCompatJs && isGutenbergPage ) {
|
||||
console.warn( __( 'Font Awesome Plugin cannot enable the Icon Chooser on a page that includes the block editor (Gutenberg) because it is not compatible with your WordPress installation. Upgrading to at least WordPress 5.4.6 will probably resolve this.', 'font-awesome' ) )
|
||||
} else {
|
||||
Promise.all([
|
||||
import('./chooser'),
|
||||
import('./chooser/handleQuery'),
|
||||
import('./chooser/getUrlText')
|
||||
])
|
||||
.then(([{ setupIconChooser }, { default: configureQueryHandler }, { default: getUrlText } ]) => {
|
||||
const kitToken = get(initialData, 'options.kitToken')
|
||||
const version = get(initialData, 'options.version')
|
||||
|
||||
const params = {
|
||||
...initialData,
|
||||
kitToken,
|
||||
version,
|
||||
getUrlText,
|
||||
pro: get(initialData, 'options.usePro')
|
||||
}
|
||||
|
||||
const handleQuery = configureQueryHandler(params)
|
||||
|
||||
const { setupClassicEditorIconChooser } = setupIconChooser({ ...params, handleQuery })
|
||||
|
||||
/**
|
||||
* Tiny MCE will probably be loaded later, but since this code runs async,
|
||||
* we can't guarantee the timing. So if this runs first, it will set this
|
||||
* global to a function that the post-tiny-mce inline code can invoke.
|
||||
* But if that code runs first, it will set this global to some truthy value,
|
||||
* which tells us to invoke this setup immediately.
|
||||
*/
|
||||
if( window['__FontAwesomeOfficialPlugin__setupClassicEditorIconChooser'] ) {
|
||||
setupClassicEditorIconChooser()
|
||||
} else {
|
||||
window['__FontAwesomeOfficialPlugin__setupClassicEditorIconChooser'] = setupClassicEditorIconChooser
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error( __( 'Font Awesome plugin error when initializing Icon Chooser', 'font-awesome' ), error )
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,29 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import ErrorBoundary from './ErrorBoundary'
|
||||
import FontAwesomeAdminView from './FontAwesomeAdminView'
|
||||
import { Provider } from 'react-redux'
|
||||
import domReady from '@wordpress/dom-ready'
|
||||
|
||||
const isAtLeastReact18 = React.version.split('.')[0] >= 18
|
||||
|
||||
export default function (store) {
|
||||
domReady(() =>
|
||||
ReactDOM.render(
|
||||
const container = document.getElementById('font-awesome-admin')
|
||||
|
||||
domReady(() => {
|
||||
const app = (
|
||||
<ErrorBoundary>
|
||||
<Provider store={store}>
|
||||
<FontAwesomeAdminView />
|
||||
</Provider>
|
||||
</ErrorBoundary>,
|
||||
document.getElementById('font-awesome-admin')
|
||||
)
|
||||
</ErrorBoundary>
|
||||
)
|
||||
|
||||
if (isAtLeastReact18) {
|
||||
createRoot(container).render(app)
|
||||
} else {
|
||||
ReactDOM.render(app, container)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import ConflictDetectionReporter from './ConflictDetectionReporter'
|
||||
import { dom } from '@fortawesome/fontawesome-svg-core'
|
||||
import { Provider } from 'react-redux'
|
||||
|
@ -24,15 +24,15 @@ export function mountConflictDetectionReporter(store) {
|
|||
faStyle.appendChild(cssText)
|
||||
|
||||
const shadowContainer = document.createElement('DIV')
|
||||
const root = createRoot(shadowContainer)
|
||||
|
||||
shadow.appendChild(faStyle)
|
||||
shadow.appendChild(shadowContainer)
|
||||
|
||||
ReactDOM.render(
|
||||
root.render(
|
||||
<Provider store={store}>
|
||||
<ConflictDetectionReporter />
|
||||
</Provider>,
|
||||
shadowContainer
|
||||
</Provider>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import { test as setup, expect } from '@wordpress/e2e-test-utils-playwright'
|
||||
import '../support/env.js'
|
||||
|
||||
const authFile = 'src/playwright/.auth/state.json'
|
||||
|
||||
setup('authenticate', async ({ page }) => {
|
||||
await page.goto('/wp-login.php')
|
||||
await page.getByLabel('Username or Email Address').fill(process.env.WP_ADMIN_USERNAME)
|
||||
await page.getByLabel('Password', { exact: true }).fill(process.env.WP_ADMIN_PASSWORD)
|
||||
await page.getByRole('button', { name: 'Log In' }).click()
|
||||
await page.waitForURL('**/wp-admin/')
|
||||
await page.context().storageState({ path: authFile })
|
||||
})
|
|
@ -0,0 +1,32 @@
|
|||
import { test as setup, expect } from '@wordpress/e2e-test-utils-playwright'
|
||||
import '../support/env.js'
|
||||
|
||||
const CONFIG_ROUTE_PATTERN = '**/font-awesome/v1/config'
|
||||
const API_ROUTE_PATTERN = '**/font-awesome/v1/api*'
|
||||
|
||||
setup('pro kit', async ({ page }) => {
|
||||
expect(process.env.API_TOKEN).toBeTruthy()
|
||||
expect(process.env.KIT_TOKEN).toBeTruthy()
|
||||
|
||||
await page.goto('/wp-admin/admin.php?page=font-awesome')
|
||||
await page.locator('label').filter({ hasText: 'Use A Kit' }).click()
|
||||
|
||||
const allText = await page.getByRole('heading').allTextContents()
|
||||
|
||||
await page.locator('label').filter({ hasText: 'API Token' }).fill(process.env.API_TOKEN)
|
||||
const saveAPITokenResponsePromise = page.waitForResponse(CONFIG_ROUTE_PATTERN)
|
||||
await page.getByRole('button', { name: 'Save API Token' }).click()
|
||||
await saveAPITokenResponsePromise
|
||||
|
||||
const kitsResponsePromise = page.waitForResponse(API_ROUTE_PATTERN)
|
||||
|
||||
await page.getByRole('button').filter({ hasText: 'kits data' }).click()
|
||||
await kitsResponsePromise
|
||||
|
||||
await page.locator('select').selectOption(process.env.KIT_TOKEN)
|
||||
|
||||
const saveSettingsResponsePromise = page.waitForResponse(CONFIG_ROUTE_PATTERN)
|
||||
|
||||
await page.getByRole('button').filter({ hasText: 'Save Changes' }).click()
|
||||
await saveSettingsResponsePromise
|
||||
})
|
|
@ -0,0 +1,22 @@
|
|||
import { test as setup, expect, RequestUtils } from '@wordpress/e2e-test-utils-playwright'
|
||||
import mysql from 'mysql2/promise'
|
||||
import { prepareRestApi } from '../support/testHelpers'
|
||||
|
||||
setup('reset', async ({ storageState, baseURL }) => {
|
||||
const { requestUtils, requestContext } = await prepareRestApi({ storageState, baseURL })
|
||||
await requestUtils.deactivatePlugin('font-awesome')
|
||||
|
||||
const connection = await mysql.createConnection({
|
||||
host: 'localhost',
|
||||
user: process.env.WORDPRESS_DB_USER,
|
||||
password: process.env.WORDPRESS_DB_PASSWORD,
|
||||
database: process.env.WORDPRESS_DB_NAME
|
||||
})
|
||||
|
||||
const sql = 'DELETE FROM `wp_options` WHERE `option_name` = ? LIMIT 1'
|
||||
await connection.execute(sql, ['font-awesome'])
|
||||
await connection.execute(sql, ['font-awesome-conflict-detection'])
|
||||
await connection.execute(sql, ['font-awesome-releases'])
|
||||
await requestUtils.activatePlugin('font-awesome')
|
||||
await requestContext.dispose()
|
||||
})
|
|
@ -0,0 +1,7 @@
|
|||
import dotenv from 'dotenv'
|
||||
import path from 'path'
|
||||
|
||||
const ROOT_DIR = path.resolve(__dirname, '../../../..')
|
||||
|
||||
dotenv.config({ path: path.resolve(ROOT_DIR, '.env'), override: true })
|
||||
dotenv.config({ path: path.resolve(ROOT_DIR, '.env.local'), override: true })
|
|
@ -0,0 +1,18 @@
|
|||
import { request } from '@playwright/test'
|
||||
import { RequestUtils } from '@wordpress/e2e-test-utils-playwright'
|
||||
|
||||
export async function prepareRestApi({ baseURL, storageState }) {
|
||||
const requestContext = await request.newContext({
|
||||
baseURL
|
||||
})
|
||||
|
||||
const storageStatePath = typeof storageState === 'string' ? storageState : undefined
|
||||
|
||||
const requestUtils = new RequestUtils(requestContext, {
|
||||
storageStatePath
|
||||
})
|
||||
|
||||
await requestUtils.setupRest()
|
||||
|
||||
return { requestUtils, requestContext }
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import { expect, test } from '@wordpress/e2e-test-utils-playwright'
|
||||
import { prepareRestApi } from '../support/testHelpers'
|
||||
|
||||
const QUERY = 'query { search(version: "6.x", query: "coffee", first: 1) { id } }'
|
||||
|
||||
// A regression test to ensure that the former way of sending query requests
|
||||
// to the plugin's /api endpoint still works, though it's no longer the recommended
|
||||
// way to do it.
|
||||
//
|
||||
// This test can only be expected to work when mod_security is disabled,
|
||||
// since making a request to the API endpoint with a default (text/plain)
|
||||
// MIME type is known to result in a 403 due to the OWASP default core ruleset
|
||||
// as of OWASP 4.3.0.
|
||||
test('query as plain text', async ({ storageState, baseURL }) => {
|
||||
expect(process.env.ENABLE_MOD_SECURITY).toEqual('false')
|
||||
|
||||
const { requestUtils, requestContext } = await prepareRestApi({ storageState, baseURL })
|
||||
|
||||
const url = `http://${process.env.WP_DOMAIN}/wp-json/font-awesome/v1/api?_locale=user`
|
||||
|
||||
const response = await requestUtils.request.fetch(url, {
|
||||
method: 'POST',
|
||||
data: QUERY,
|
||||
headers: {
|
||||
'X-WP-Nonce': requestUtils.storageState.nonce
|
||||
}
|
||||
})
|
||||
|
||||
expect(response.status()).toEqual(200)
|
||||
|
||||
const responseObj = await response.json()
|
||||
|
||||
expect(responseObj).toHaveProperty('data.search')
|
||||
})
|
|
@ -0,0 +1,15 @@
|
|||
import { expect, test } from '@wordpress/e2e-test-utils-playwright'
|
||||
|
||||
test('change technology', async ({ page }) => {
|
||||
await page.goto('/wp-admin/admin.php?page=font-awesome')
|
||||
|
||||
const preferenceCheckResponsePromise = page.waitForResponse('**/font-awesome/v1/preference-check')
|
||||
|
||||
await page.getByText('SVG').click()
|
||||
|
||||
await preferenceCheckResponsePromise
|
||||
|
||||
const saveChangesResponsePromise = page.waitForResponse('**/font-awesome/v1/config')
|
||||
await page.getByRole('button', { name: 'Save Changes' }).click()
|
||||
await saveChangesResponsePromise
|
||||
})
|
|
@ -0,0 +1,53 @@
|
|||
import { Editor, expect, test } from '@wordpress/e2e-test-utils-playwright'
|
||||
|
||||
test.describe('full site editor', async () => {
|
||||
test.use({
|
||||
editor: async ({ page }, use) => {
|
||||
await use(new Editor({ page }))
|
||||
}
|
||||
})
|
||||
|
||||
test('insert with icon chooser', async ({ page, editor, pageUtils }) => {
|
||||
const pageLoadPromise = page.waitForResponse('**/wp/v2/pages*')
|
||||
|
||||
await page.goto('/wp-admin/site-editor.php?canvas=edit')
|
||||
|
||||
await pageLoadPromise
|
||||
|
||||
const getStartedCount = await page.getByRole('button', { name: 'Get started' }).count()
|
||||
|
||||
if (getStartedCount > 0) {
|
||||
await page.getByRole('button', { name: 'Get started' }).click()
|
||||
}
|
||||
|
||||
await editor.insertBlock({
|
||||
name: 'core/paragraph'
|
||||
})
|
||||
await page.keyboard.type('Here comes an icon: ')
|
||||
|
||||
await editor.clickBlockToolbarButton('More')
|
||||
|
||||
await pageUtils.pressKeys('Enter', 1)
|
||||
|
||||
await page.waitForSelector('fa-icon-chooser input#search')
|
||||
|
||||
const searchResponsePromise = page.waitForResponse('**/font-awesome/v1/api*')
|
||||
|
||||
await page.locator('fa-icon-chooser input#search').fill('coffee')
|
||||
|
||||
await searchResponsePromise
|
||||
|
||||
await page.locator('fa-icon-chooser button.icon').first().click()
|
||||
|
||||
let blocks = null
|
||||
|
||||
try {
|
||||
// On WP 6.0.8, this throws an exception, but it's a false negative.
|
||||
// So, if there's a succesfully call of getBlocks(), we want to
|
||||
// assert its results. But if that fails, don't fail the whole test.
|
||||
blocks = await editor.getBlocks()
|
||||
expect(blocks).toHaveLength(1)
|
||||
expect(blocks[0].attributes.content).toMatch(/\[icon.*?\]$/)
|
||||
} catch (_e) {}
|
||||
})
|
||||
})
|
|
@ -0,0 +1,48 @@
|
|||
import { Editor, test, expect, login, RequestUtils } from '@wordpress/e2e-test-utils-playwright'
|
||||
|
||||
test.describe('blockEditorIconChooser', async () => {
|
||||
test.beforeEach(async ({ admin }) => {
|
||||
await admin.createNewPost()
|
||||
})
|
||||
|
||||
test.use({
|
||||
editor: async ({ page }, use) => {
|
||||
await use(new Editor({ page }))
|
||||
}
|
||||
})
|
||||
|
||||
test('search and select from icon chooser', async ({ editor, page, pageUtils }) => {
|
||||
await editor.insertBlock({
|
||||
name: 'core/paragraph'
|
||||
})
|
||||
await page.keyboard.type('Here comes an icon: ')
|
||||
|
||||
await editor.clickBlockToolbarButton('More')
|
||||
|
||||
await pageUtils.pressKeys('Enter', 1)
|
||||
|
||||
await page.waitForSelector('fa-icon-chooser input#search')
|
||||
|
||||
const searchResponsePromise = page.waitForResponse('**/font-awesome/v1/api*')
|
||||
|
||||
await page.locator('fa-icon-chooser input#search').fill('coffee')
|
||||
|
||||
await searchResponsePromise
|
||||
|
||||
await page.locator('fa-icon-chooser button.icon').first().click()
|
||||
|
||||
let blocks = null
|
||||
|
||||
try {
|
||||
// On WP 6.0.8, this throws an exception, but it's a false negative.
|
||||
// So, if there's a succesfully call of getBlocks(), we want to
|
||||
// assert its results. But if that fails, don't fail the whole test.
|
||||
blocks = await editor.getBlocks()
|
||||
expect(blocks).toHaveLength(1)
|
||||
expect(blocks[0].attributes.content).toMatch(/\[icon.*?\]$/)
|
||||
} catch (_e) {}
|
||||
|
||||
// The loading of the icon chooser should not have messed up the lodash globals.
|
||||
await expect(page.evaluate(() => 'undefined' !== typeof _ && 'undefined' !== typeof lodash)).toBeTruthy()
|
||||
})
|
||||
})
|
|
@ -0,0 +1,41 @@
|
|||
import { expect, test } from '@wordpress/e2e-test-utils-playwright'
|
||||
|
||||
test.describe('conflictScanner', async () => {
|
||||
test.beforeEach(async ({ requestUtils }) => {
|
||||
await requestUtils.activatePlugin('plugin-gamma')
|
||||
})
|
||||
|
||||
test.afterEach(async ({ requestUtils }) => {
|
||||
await requestUtils.deactivatePlugin('plugin-gamma')
|
||||
})
|
||||
|
||||
test('start conflict detection scanner, detect, and block', async ({ page }) => {
|
||||
await page.goto('/wp-admin/admin.php?page=font-awesome')
|
||||
await page.getByRole('button', { name: 'Troubleshoot' }).click()
|
||||
const scannerStartResponsePromise = page.waitForResponse('**/font-awesome/v1/conflict-detection/until')
|
||||
await page.getByRole('button', { name: 'Enable scanner for 10 minutes' }).click()
|
||||
await scannerStartResponsePromise
|
||||
|
||||
await expect(page.getByRole('heading', { name: 'Font Awesome Conflict Scanner' })).toBeVisible()
|
||||
|
||||
await expect(page.locator('span').filter({ hasText: 'minutes left to browse' }).first()).toBeVisible()
|
||||
|
||||
const scannerReportResponsePromise = page.waitForResponse('**/font-awesome/v1/conflict-detection/conflicts')
|
||||
await page.goto('/')
|
||||
await expect(page.getByRole('heading', { name: 'Plugin Gamma' })).toBeVisible()
|
||||
|
||||
await scannerReportResponsePromise
|
||||
await expect(page.getByRole('heading', { name: 'Font Awesome Conflict Scanner' })).toBeVisible()
|
||||
|
||||
await expect(page.getByText('Page scan complete')).toBeVisible()
|
||||
await expect(page.getByText('1 new conflicts found on this page')).toBeVisible()
|
||||
await expect(page.getByText('1 total found')).toBeVisible()
|
||||
await page.getByRole('link', { name: 'manage' }).click()
|
||||
await page.getByRole('row', { name: 'link https://cdn.jsdelivr.net' }).locator('svg').nth(1).click()
|
||||
const saveChangesResponsePromise = page.waitForResponse('**/font-awesome/v1/conflict-detection/conflicts/blocklist')
|
||||
await page.getByRole('button', { name: 'Save Changes' }).click()
|
||||
await saveChangesResponsePromise
|
||||
await page.goto('/')
|
||||
await expect(page.getByText('0 new conflicts found on this page')).toBeVisible()
|
||||
})
|
||||
})
|
|
@ -0,0 +1,72 @@
|
|||
const CACHE_KEY_PREFIX = 'wp-font-awesome-cache'
|
||||
|
||||
function buildPrefixedKey(key) {
|
||||
return `${CACHE_KEY_PREFIX}-${key}`
|
||||
}
|
||||
|
||||
// This removes all items in localStorage whose keys begin
|
||||
// with this modules CACHE_KEY_PREFIX.
|
||||
export function clearQueryCache() {
|
||||
if (!window?.localStorage) return
|
||||
|
||||
if (localStorage.length === 0) return
|
||||
|
||||
for (let i = localStorage.length - 1; i >= 0; i--) {
|
||||
const key = localStorage.key(i)
|
||||
if (key.startsWith(CACHE_KEY_PREFIX)) {
|
||||
localStorage.removeItem(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function remove(prefixedCacheKey) {
|
||||
if ('function' !== typeof window?.localStorage?.removeItem) return
|
||||
localStorage.removeItem(prefixedCacheKey)
|
||||
}
|
||||
|
||||
// Takes a cache that is *not* prefixed.
|
||||
// Expects to be able to add its prefix, and get an item from localStorage
|
||||
// which must be JSON-parseable.
|
||||
//
|
||||
// On success, returns the result of the cache value after JSON.parse().
|
||||
// On failure, returns undefined: if there's no matching key, or an error
|
||||
// in JSON parsing.
|
||||
export function get(key) {
|
||||
if ('function' !== typeof window?.localStorage?.getItem) return
|
||||
|
||||
const prefixedCacheKey = buildPrefixedKey(key)
|
||||
|
||||
const cacheValueJson = localStorage.getItem(prefixedCacheKey)
|
||||
|
||||
try {
|
||||
const cacheValue = JSON.parse(cacheValueJson)
|
||||
|
||||
if (cacheValue) {
|
||||
return cacheValue
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
remove(prefixedCacheKey)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Takes a key that has not been prefixed, and a value that must be passable
|
||||
// as an argument to JSON.stringify().
|
||||
//
|
||||
// Prefixes the key, and stores the JSON-stringified value in localStorage.
|
||||
//
|
||||
// Always returns undefined.
|
||||
export function set(key, value) {
|
||||
if ('function' !== typeof window?.localStorage?.setItem) return
|
||||
|
||||
const prefixedCacheKey = buildPrefixedKey(key)
|
||||
|
||||
try {
|
||||
const valueJson = JSON.stringify(value)
|
||||
localStorage.setItem(prefixedCacheKey, valueJson)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
const reportWebVitals = onPerfEntry => {
|
||||
const reportWebVitals = (onPerfEntry) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
getCLS(onPerfEntry)
|
||||
getFID(onPerfEntry)
|
||||
getFCP(onPerfEntry)
|
||||
getLCP(onPerfEntry)
|
||||
getTTFB(onPerfEntry)
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
||||
export default reportWebVitals
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import Enzyme from 'enzyme'
|
||||
import Adapter from 'enzyme-adapter-react-16'
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
import { insertBlock, createNewPost, enablePageDialogAccept, pressKeyTimes, clickBlockToolbarButton, getAllBlocks, loginUser } from '@wordpress/e2e-test-utils'
|
||||
|
||||
jest.setTimeout(15000)
|
||||
|
||||
let config = null
|
||||
|
||||
describe('blockEditorIconChooser', () => {
|
||||
beforeAll(async () => {
|
||||
enablePageDialogAccept()
|
||||
await loginUser(process.env.WP_USERNAME, process.env.WP_PASSWORD)
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await createNewPost()
|
||||
config = await page.evaluate(() => window.__FontAwesomeOfficialPlugin__)
|
||||
})
|
||||
|
||||
test('works', async () => {
|
||||
await insertBlock( 'Paragraph' )
|
||||
await pressKeyTimes('Space', 1)
|
||||
await clickBlockToolbarButton('More')
|
||||
await pressKeyTimes('Enter', 1)
|
||||
|
||||
const searchInput = await page.waitForFunction(() => {
|
||||
const chooser = document.querySelector('fa-icon-chooser')
|
||||
const shadowRoot = chooser && chooser.shadowRoot
|
||||
return shadowRoot.querySelector('input#search')
|
||||
})
|
||||
|
||||
await searchInput.focus()
|
||||
await searchInput.type('coffee', { delay: 100 })
|
||||
|
||||
await page.waitForResponse(
|
||||
`${config.apiUrl}/api?_locale=user`
|
||||
);
|
||||
|
||||
const firstIcon = await page.waitForFunction(() => {
|
||||
return document.querySelector('fa-icon-chooser').shadowRoot.querySelector('button.icon')
|
||||
})
|
||||
|
||||
await firstIcon.click()
|
||||
|
||||
const blocks = await getAllBlocks()
|
||||
expect(blocks).toHaveLength(1)
|
||||
expect(blocks[0].attributes.content).toMatch(/\[icon.*?\]$/)
|
||||
})
|
||||
})
|
|
@ -1,32 +0,0 @@
|
|||
import { visitAdminPage } from '@wordpress/e2e-test-utils'
|
||||
import { resetOptions } from '../testUtil'
|
||||
|
||||
describe('changeTechnology', () => {
|
||||
beforeAll(async () => {
|
||||
await visitAdminPage('options-general.php', 'page=font-awesome')
|
||||
await resetOptions(page)
|
||||
await page.reload()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await resetOptions(page)
|
||||
})
|
||||
|
||||
test('works', async () => {
|
||||
const useCdnInput = await page.$('#select_use_cdn')
|
||||
const useKitInput = await page.$('#select_use_kits')
|
||||
|
||||
expect(await useCdnInput.evaluate( i => i.checked )).toBe(true)
|
||||
expect(await useKitInput.evaluate( i => i.checked )).toBe(false)
|
||||
|
||||
const techSvgInput = await page.$('#code_edit_tech_svg')
|
||||
expect(await techSvgInput.evaluate( i => i.checked )).toBe(false)
|
||||
await techSvgInput.click()
|
||||
|
||||
const submitBefore = await page.waitForSelector('#submit:enabled')
|
||||
await submitBefore.click()
|
||||
|
||||
await page.waitForSelector('#submit:disabled')
|
||||
expect(await techSvgInput.evaluate( i => i.checked )).toBe(true)
|
||||
})
|
||||
})
|
|
@ -1,12 +1,9 @@
|
|||
import axios from 'axios'
|
||||
import toPairs from 'lodash/toPairs'
|
||||
import size from 'lodash/size'
|
||||
import get from 'lodash/get'
|
||||
import find from 'lodash/find'
|
||||
import { toPairs, size, get, has, find } from 'lodash'
|
||||
import reportRequestError, { redactRequestData, redactHeaders } from '../util/reportRequestError'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import has from 'lodash/has'
|
||||
import sliceJson from '../util/sliceJson'
|
||||
import { clearQueryCache } from '../queryCache'
|
||||
|
||||
const restApiAxios = axios.create()
|
||||
|
||||
|
@ -20,13 +17,16 @@ export const CONFLICT_DETECTION_SCANNER_DURATION_MIN = 10
|
|||
// (which would just be exactly "now").
|
||||
const CONFLICT_DETECTION_SCANNER_DEACTIVATION_DELTA_MS = 1
|
||||
|
||||
const COULD_NOT_SAVE_CHANGES_MESSAGE = __( 'Couldn\'t save those changes', 'font-awesome' )
|
||||
const REJECTED_METHOD_COULD_NOT_SAVE_CHANGES_MESSAGE = __( 'Changes not saved because your WordPress server does not allow this kind of request. Look for details in the browser console.', 'font-awesome' )
|
||||
const COULD_NOT_CHECK_PREFERENCES_MESSAGE = __( 'Couldn\'t check preferences', 'font-awesome' )
|
||||
const COULD_NOT_SAVE_CHANGES_MESSAGE = __("Couldn't save those changes", 'font-awesome')
|
||||
const REJECTED_METHOD_COULD_NOT_SAVE_CHANGES_MESSAGE = __(
|
||||
'Changes not saved because your WordPress server does not allow this kind of request. Look for details in the browser console.',
|
||||
'font-awesome'
|
||||
)
|
||||
const COULD_NOT_CHECK_PREFERENCES_MESSAGE = __("Couldn't check preferences", 'font-awesome')
|
||||
const NO_RESPONSE_MESSAGE = __('A request to your WordPress server never received a response', 'font-awesome')
|
||||
const REQUEST_FAILED_MESSAGE = __('A request to your WordPress server failed', 'font-awesome')
|
||||
const COULD_NOT_START_SCANNER_MESSAGE = __( 'Couldn\'t start the scanner', 'font-awesome' )
|
||||
const COULD_NOT_SNOOZE_MESSAGE = __( 'Couldn\'t snooze', 'font-awesome' )
|
||||
const COULD_NOT_START_SCANNER_MESSAGE = __("Couldn't start the scanner", 'font-awesome')
|
||||
const COULD_NOT_SNOOZE_MESSAGE = __("Couldn't snooze", 'font-awesome')
|
||||
|
||||
export function preprocessResponse(response) {
|
||||
const confirmed = has(response, 'headers.fontawesome-confirmation')
|
||||
|
@ -42,9 +42,7 @@ export function preprocessResponse( response ) {
|
|||
|
||||
const foundUnexpectedData = 'string' === typeof data && size(data) > 0
|
||||
|
||||
const sliced = foundUnexpectedData
|
||||
? sliceJson( data )
|
||||
: {}
|
||||
const sliced = foundUnexpectedData ? sliceJson(data) : {}
|
||||
|
||||
// Fixup the response data if garbage was fixed
|
||||
if (foundUnexpectedData) {
|
||||
|
@ -149,8 +147,8 @@ export function preprocessResponse( response ) {
|
|||
}
|
||||
|
||||
restApiAxios.interceptors.response.use(
|
||||
response => preprocessResponse( response ),
|
||||
error => {
|
||||
(response) => preprocessResponse(response),
|
||||
(error) => {
|
||||
if (error.response) {
|
||||
error.response = preprocessResponse(error.response)
|
||||
error.uiMessage = get(error, 'response.uiMessage')
|
||||
|
@ -255,15 +253,14 @@ export function submitPendingUnregisteredClientDeletions() {
|
|||
})
|
||||
}
|
||||
|
||||
return restApiAxios.delete(
|
||||
`${apiUrl}/conflict-detection/conflicts`,
|
||||
{
|
||||
return restApiAxios
|
||||
.delete(`${apiUrl}/conflict-detection/conflicts`, {
|
||||
data: deleteList,
|
||||
headers: {
|
||||
'X-WP-Nonce': apiNonce
|
||||
}
|
||||
}
|
||||
).then(response => {
|
||||
})
|
||||
.then((response) => {
|
||||
const { status, data, falsePositive } = response
|
||||
|
||||
if (falsePositive) {
|
||||
|
@ -276,7 +273,8 @@ export function submitPendingUnregisteredClientDeletions() {
|
|||
message: ''
|
||||
})
|
||||
}
|
||||
}).catch(handleError)
|
||||
})
|
||||
.catch(handleError)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,15 +302,13 @@ export function submitPendingBlocklist() {
|
|||
})
|
||||
}
|
||||
|
||||
return restApiAxios.put(
|
||||
`${apiUrl}/conflict-detection/conflicts/blocklist`,
|
||||
blocklist,
|
||||
{
|
||||
return restApiAxios
|
||||
.post(`${apiUrl}/conflict-detection/conflicts/blocklist`, blocklist, {
|
||||
headers: {
|
||||
'X-WP-Nonce': apiNonce
|
||||
}
|
||||
}
|
||||
).then(response => {
|
||||
})
|
||||
.then((response) => {
|
||||
const { status, data, falsePositive } = response
|
||||
|
||||
if (falsePositive) {
|
||||
|
@ -325,7 +321,8 @@ export function submitPendingBlocklist() {
|
|||
message: ''
|
||||
})
|
||||
}
|
||||
}).catch(handleError)
|
||||
})
|
||||
.catch(handleError)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -342,7 +339,8 @@ export function checkPreferenceConflicts() {
|
|||
})
|
||||
}
|
||||
|
||||
return restApiAxios.post(
|
||||
return restApiAxios
|
||||
.post(
|
||||
`${apiUrl}/preference-check`,
|
||||
{ ...options, ...pendingOptions },
|
||||
{
|
||||
|
@ -350,7 +348,8 @@ export function checkPreferenceConflicts() {
|
|||
'X-WP-Nonce': apiNonce
|
||||
}
|
||||
}
|
||||
).then(response => {
|
||||
)
|
||||
.then((response) => {
|
||||
const { data, falsePositive } = response
|
||||
|
||||
if (falsePositive) {
|
||||
|
@ -363,7 +362,8 @@ export function checkPreferenceConflicts() {
|
|||
detectedConflicts: data
|
||||
})
|
||||
}
|
||||
}).catch(handleError)
|
||||
})
|
||||
.catch(handleError)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,7 +374,7 @@ export function chooseAwayFromKitConfig({ activeKitToken }) {
|
|||
dispatch({
|
||||
type: 'CHOOSE_AWAY_FROM_KIT_CONFIG',
|
||||
activeKitToken,
|
||||
concreteVersion: get(releases, 'latest_version_6')
|
||||
concreteVersion: get(releases, 'latest_version_7')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -391,6 +391,8 @@ export function queryKits() {
|
|||
|
||||
dispatch({ type: 'KITS_QUERY_START' })
|
||||
|
||||
clearQueryCache()
|
||||
|
||||
const handleKitsQueryError = ({ uiMessage }) => {
|
||||
dispatch({
|
||||
type: 'KITS_QUERY_END',
|
||||
|
@ -403,33 +405,21 @@ export function queryKits() {
|
|||
dispatch({
|
||||
type: 'OPTIONS_FORM_SUBMIT_END',
|
||||
success: false,
|
||||
message: uiMessage || __( 'Couldn\'t update latest kit settings', 'font-awesome' )
|
||||
message: uiMessage || __("Couldn't update latest kit settings", 'font-awesome')
|
||||
})
|
||||
}
|
||||
|
||||
return restApiAxios.post(
|
||||
return restApiAxios
|
||||
.post(
|
||||
`${apiUrl}/api`,
|
||||
`query {
|
||||
me {
|
||||
kits {
|
||||
name
|
||||
version
|
||||
technologySelected
|
||||
licenseSelected
|
||||
minified
|
||||
token
|
||||
shimEnabled
|
||||
autoAccessibilityEnabled
|
||||
status
|
||||
}
|
||||
}
|
||||
}`,
|
||||
'query { me { kits { name version technologySelected licenseSelected minified token shimEnabled autoAccessibilityEnabled status }}}',
|
||||
{
|
||||
headers: {
|
||||
'X-WP-Nonce': apiNonce
|
||||
}
|
||||
}
|
||||
).then(response => {
|
||||
)
|
||||
.then((response) => {
|
||||
if (response.falsePositive) return handleKitsQueryError(response)
|
||||
|
||||
const data = get(response, 'data.data')
|
||||
|
@ -492,11 +482,13 @@ export function queryKits() {
|
|||
|
||||
dispatch({ type: 'OPTIONS_FORM_SUBMIT_START' })
|
||||
|
||||
return restApiAxios.put(
|
||||
return restApiAxios
|
||||
.post(
|
||||
`${apiUrl}/config`,
|
||||
{
|
||||
options: {
|
||||
...options, ...optionsUpdate
|
||||
...options,
|
||||
...optionsUpdate
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -504,7 +496,8 @@ export function queryKits() {
|
|||
'X-WP-Nonce': apiNonce
|
||||
}
|
||||
}
|
||||
).then(response => {
|
||||
)
|
||||
.then((response) => {
|
||||
const { data, falsePositive } = response
|
||||
|
||||
if (falsePositive) return handleKitUpdateError(response)
|
||||
|
@ -515,8 +508,10 @@ export function queryKits() {
|
|||
success: true,
|
||||
message: __('Kit changes saved', 'font-awesome')
|
||||
})
|
||||
}).catch(handleKitUpdateError)
|
||||
}).catch(handleKitsQueryError)
|
||||
})
|
||||
.catch(handleKitUpdateError)
|
||||
})
|
||||
.catch(handleKitsQueryError)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -534,7 +529,8 @@ export function submitPendingOptions() {
|
|||
})
|
||||
}
|
||||
|
||||
return restApiAxios.put(
|
||||
return restApiAxios
|
||||
.post(
|
||||
`${apiUrl}/config`,
|
||||
{ options: { ...options, ...pendingOptions } },
|
||||
{
|
||||
|
@ -542,7 +538,8 @@ export function submitPendingOptions() {
|
|||
'X-WP-Nonce': apiNonce
|
||||
}
|
||||
}
|
||||
).then(response => {
|
||||
)
|
||||
.then((response) => {
|
||||
const { data, falsePositive } = response
|
||||
|
||||
if (falsePositive) {
|
||||
|
@ -555,7 +552,8 @@ export function submitPendingOptions() {
|
|||
message: __('Changes saved', 'font-awesome')
|
||||
})
|
||||
}
|
||||
}).catch(handleError)
|
||||
})
|
||||
.catch(handleError)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -573,7 +571,8 @@ export function updateApiToken({ apiToken = false, runQueryKits = false }) {
|
|||
})
|
||||
}
|
||||
|
||||
return restApiAxios.put(
|
||||
return restApiAxios
|
||||
.post(
|
||||
`${apiUrl}/config`,
|
||||
{ options: { ...options, apiToken } },
|
||||
{
|
||||
|
@ -581,7 +580,8 @@ export function updateApiToken({ apiToken = false, runQueryKits = false }) {
|
|||
'X-WP-Nonce': apiNonce
|
||||
}
|
||||
}
|
||||
).then(response => {
|
||||
)
|
||||
.then((response) => {
|
||||
const { data, falsePositive } = response
|
||||
|
||||
if (falsePositive) {
|
||||
|
@ -598,7 +598,8 @@ export function updateApiToken({ apiToken = false, runQueryKits = false }) {
|
|||
return dispatch(queryKits())
|
||||
}
|
||||
}
|
||||
}).catch(handleError)
|
||||
})
|
||||
.catch(handleError)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -641,16 +642,13 @@ export function reportDetectedConflicts({ nodesTested = {} }) {
|
|||
})
|
||||
}
|
||||
|
||||
return restApiAxios.post(
|
||||
`${apiUrl}/conflict-detection/conflicts`,
|
||||
payload,
|
||||
{
|
||||
return restApiAxios
|
||||
.post(`${apiUrl}/conflict-detection/conflicts`, payload, {
|
||||
headers: {
|
||||
'X-WP-Nonce': apiNonce
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
})
|
||||
.then((response) => {
|
||||
const { status, data, falsePositive } = response
|
||||
|
||||
if (falsePositive) {
|
||||
|
@ -664,7 +662,7 @@ export function reportDetectedConflicts({ nodesTested = {} }) {
|
|||
* response with garbage in it had an erroneous HTTP 200 status
|
||||
* on it, but no parseable JSON, which is equivalent to a 204.
|
||||
*/
|
||||
data: ( 204 === status || 0 === size(data) ) ? null : data
|
||||
data: 204 === status || 0 === size(data) ? null : data
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -675,47 +673,6 @@ export function reportDetectedConflicts({ nodesTested = {} }) {
|
|||
}
|
||||
}
|
||||
|
||||
export function snoozeV3DeprecationWarning() {
|
||||
return (dispatch, getState) => {
|
||||
const { apiNonce, apiUrl } = getState()
|
||||
|
||||
dispatch({ type: 'SNOOZE_V3DEPRECATION_WARNING_START' })
|
||||
|
||||
const handleError = ({ uiMessage }) => {
|
||||
dispatch({
|
||||
type: 'SNOOZE_V3DEPRECATION_WARNING_END',
|
||||
success: false,
|
||||
message: uiMessage || COULD_NOT_SNOOZE_MESSAGE
|
||||
})
|
||||
}
|
||||
|
||||
return restApiAxios.put(
|
||||
`${apiUrl}/v3deprecation`,
|
||||
{ snooze: true },
|
||||
{
|
||||
headers: {
|
||||
'X-WP-Nonce': apiNonce
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
const { falsePositive } = response
|
||||
|
||||
if ( falsePositive ) {
|
||||
handleError(response)
|
||||
} else {
|
||||
dispatch({
|
||||
type: 'SNOOZE_V3DEPRECATION_WARNING_END',
|
||||
success: true,
|
||||
snooze: true,
|
||||
message: ''
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(handleError)
|
||||
}
|
||||
}
|
||||
|
||||
export function setActiveAdminTab(tab) {
|
||||
return {
|
||||
type: 'SET_ACTIVE_ADMIN_TAB',
|
||||
|
@ -727,13 +684,9 @@ export function setConflictDetectionScanner({ enable = true }) {
|
|||
return function (dispatch, getState) {
|
||||
const { apiNonce, apiUrl } = getState()
|
||||
|
||||
const actionStartType = enable
|
||||
? 'ENABLE_CONFLICT_DETECTION_SCANNER_START'
|
||||
: 'DISABLE_CONFLICT_DETECTION_SCANNER_START'
|
||||
const actionStartType = enable ? 'ENABLE_CONFLICT_DETECTION_SCANNER_START' : 'DISABLE_CONFLICT_DETECTION_SCANNER_START'
|
||||
|
||||
const actionEndType = enable
|
||||
? 'ENABLE_CONFLICT_DETECTION_SCANNER_END'
|
||||
: 'DISABLE_CONFLICT_DETECTION_SCANNER_END'
|
||||
const actionEndType = enable ? 'ENABLE_CONFLICT_DETECTION_SCANNER_END' : 'DISABLE_CONFLICT_DETECTION_SCANNER_END'
|
||||
|
||||
dispatch({ type: actionStartType })
|
||||
|
||||
|
@ -745,17 +698,19 @@ export function setConflictDetectionScanner({ enable = true }) {
|
|||
})
|
||||
}
|
||||
|
||||
return restApiAxios.put(
|
||||
return restApiAxios
|
||||
.post(
|
||||
`${apiUrl}/conflict-detection/until`,
|
||||
enable
|
||||
? Math.floor((new Date((new Date()).valueOf() + (CONFLICT_DETECTION_SCANNER_DURATION_MIN * 1000 * 60))) / 1000)
|
||||
: Math.floor((new Date())/1000) - CONFLICT_DETECTION_SCANNER_DEACTIVATION_DELTA_MS,
|
||||
? Math.floor(new Date(new Date().valueOf() + CONFLICT_DETECTION_SCANNER_DURATION_MIN * 1000 * 60) / 1000)
|
||||
: Math.floor(new Date() / 1000) - CONFLICT_DETECTION_SCANNER_DEACTIVATION_DELTA_MS,
|
||||
{
|
||||
headers: {
|
||||
'X-WP-Nonce': apiNonce
|
||||
}
|
||||
}
|
||||
).then(response => {
|
||||
)
|
||||
.then((response) => {
|
||||
const { status, data, falsePositive } = response
|
||||
|
||||
if (falsePositive) {
|
||||
|
@ -767,6 +722,7 @@ export function setConflictDetectionScanner({ enable = true }) {
|
|||
success: true
|
||||
})
|
||||
}
|
||||
}).catch(handleError)
|
||||
})
|
||||
.catch(handleError)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { respondWith, resetAxiosMocks, changeImpl } from 'axios'
|
|||
import * as actions from './actions'
|
||||
import { submitPendingOptions, addPendingOption } from './actions'
|
||||
import configureMockStore from 'redux-mock-store'
|
||||
import thunk from 'redux-thunk'
|
||||
import { thunk } from 'redux-thunk'
|
||||
import reportRequestError, { MOCK_UI_MESSAGE, redactHeaders, redactRequestData } from '../util/reportRequestError'
|
||||
jest.mock('../util/reportRequestError')
|
||||
const apiUrl = '/font-awesome/v1'
|
||||
|
@ -30,14 +30,18 @@ describe('addPendingOption', () => {
|
|||
test('when multiple pending options are adjusted together, all are updated', () => {
|
||||
store.dispatch(addPendingOption({ technology: 'webfont', pseudoElements: true }))
|
||||
expect(store.getActions().length).toEqual(2)
|
||||
expect(store.getActions()[0]).toEqual(expect.objectContaining({
|
||||
expect(store.getActions()[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
type: 'ADD_PENDING_OPTION',
|
||||
change: { technology: 'webfont' }
|
||||
}))
|
||||
expect(store.getActions()[1]).toEqual(expect.objectContaining({
|
||||
})
|
||||
)
|
||||
expect(store.getActions()[1]).toEqual(
|
||||
expect.objectContaining({
|
||||
type: 'ADD_PENDING_OPTION',
|
||||
change: { pseudoElements: true }
|
||||
}))
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -74,11 +78,11 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
options: pendingOptions,
|
||||
error: {
|
||||
errors: {
|
||||
"code1": ["message1"],
|
||||
code1: ['message1']
|
||||
},
|
||||
error_data: {
|
||||
"code1": {
|
||||
"trace": 'some stack trace'
|
||||
code1: {
|
||||
trace: 'some stack trace'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +90,7 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
|
||||
respondWith({
|
||||
url: `${apiUrl}/config`,
|
||||
method: 'PUT',
|
||||
method: 'POST',
|
||||
response: {
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
|
@ -98,10 +102,13 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test('submits successfully with successful ui message and also reports error to console', done => {
|
||||
store.dispatch(submitPendingOptions()).then(() => {
|
||||
test('submits successfully with successful ui message and also reports error to console', (done) => {
|
||||
store
|
||||
.dispatch(submitPendingOptions())
|
||||
.then(() => {
|
||||
expect(reportRequestError).toHaveBeenCalledTimes(1)
|
||||
expect(reportRequestError).toHaveBeenCalledWith(expect.objectContaining({
|
||||
expect(reportRequestError).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
error: expect.objectContaining({
|
||||
errors: {
|
||||
code1: expect.anything()
|
||||
|
@ -112,9 +119,11 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
}),
|
||||
confirmed: true,
|
||||
ok: true
|
||||
}))
|
||||
})
|
||||
)
|
||||
expect(store.getActions().length).toEqual(2)
|
||||
expect(store.getActions()).toEqual(expect.arrayContaining([
|
||||
expect(store.getActions()).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'OPTIONS_FORM_SUBMIT_START'
|
||||
}),
|
||||
|
@ -124,10 +133,11 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
data,
|
||||
message: expect.stringContaining('saved')
|
||||
})
|
||||
]))
|
||||
])
|
||||
)
|
||||
done()
|
||||
})
|
||||
.catch(e => done(e))
|
||||
.catch((e) => done(e))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -143,7 +153,7 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
|
||||
respondWith({
|
||||
url: `${apiUrl}/config`,
|
||||
method: 'PUT',
|
||||
method: 'POST',
|
||||
response: {
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
|
@ -152,17 +162,22 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test('reports warning but completes successfully', done => {
|
||||
store.dispatch(submitPendingOptions()).then(() => {
|
||||
test('reports warning but completes successfully', (done) => {
|
||||
store
|
||||
.dispatch(submitPendingOptions())
|
||||
.then(() => {
|
||||
expect(reportRequestError).toHaveBeenCalledTimes(1)
|
||||
expect(reportRequestError).toHaveBeenCalledWith(expect.objectContaining({
|
||||
expect(reportRequestError).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
error: null,
|
||||
confirmed: false,
|
||||
ok: true,
|
||||
trimmed: INVALID_JSON_RESPONSE_DATA
|
||||
}))
|
||||
})
|
||||
)
|
||||
expect(store.getActions().length).toEqual(2)
|
||||
expect(store.getActions()).toEqual(expect.arrayContaining([
|
||||
expect(store.getActions()).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'OPTIONS_FORM_SUBMIT_START'
|
||||
}),
|
||||
|
@ -172,23 +187,26 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
data,
|
||||
message: expect.stringContaining('saved')
|
||||
})
|
||||
]))
|
||||
])
|
||||
)
|
||||
done()
|
||||
})
|
||||
.catch(e => done(e))
|
||||
.catch((e) => done(e))
|
||||
})
|
||||
|
||||
describe('axios request', () => {
|
||||
let mockPut = null
|
||||
let mockPost = null
|
||||
beforeEach(() => {
|
||||
mockPut = jest.fn(() => Promise.resolve({ data }))
|
||||
changeImpl({ name: 'put', fn: mockPut })
|
||||
mockPost = jest.fn(() => Promise.resolve({ data }))
|
||||
changeImpl({ name: 'post', fn: mockPost })
|
||||
})
|
||||
|
||||
test('submits pendingOptions', done => {
|
||||
store.dispatch(submitPendingOptions()).then(() => {
|
||||
expect(mockPut).toHaveBeenCalledTimes(1)
|
||||
expect(mockPut).toHaveBeenCalledWith(
|
||||
test('submits pendingOptions', (done) => {
|
||||
store
|
||||
.dispatch(submitPendingOptions())
|
||||
.then(() => {
|
||||
expect(mockPost).toHaveBeenCalledTimes(1)
|
||||
expect(mockPost).toHaveBeenCalledWith(
|
||||
`${apiUrl}/config`,
|
||||
expect.objectContaining({
|
||||
options: pendingOptions
|
||||
|
@ -201,7 +219,7 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
)
|
||||
done()
|
||||
})
|
||||
.catch(e => done(e))
|
||||
.catch((e) => done(e))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -212,7 +230,7 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
describe('when errors payload is absent', () => {
|
||||
const responseData = { foo: 42 }
|
||||
const url = `${apiUrl}/config`
|
||||
const method = 'PUT'
|
||||
const method = 'POST'
|
||||
const status = 400
|
||||
const statusText = 'Bad Request'
|
||||
const requestData = JSON.stringify({ bar: 43 })
|
||||
|
@ -238,15 +256,18 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
data: requestData,
|
||||
headers: requestHeaders
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test('displays default ui message and emits console message', done => {
|
||||
test('displays default ui message and emits console message', (done) => {
|
||||
reportRequestError.mockReturnValueOnce(null)
|
||||
store.dispatch(submitPendingOptions()).then(() => {
|
||||
store
|
||||
.dispatch(submitPendingOptions())
|
||||
.then(() => {
|
||||
expect(reportRequestError).toHaveBeenCalledTimes(1)
|
||||
expect(reportRequestError).toHaveBeenCalledWith(expect.objectContaining({
|
||||
expect(reportRequestError).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
confirmed: true,
|
||||
requestMethod: method,
|
||||
//requestData,
|
||||
|
@ -256,9 +277,11 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
responseStatus: status,
|
||||
responseStatusText: statusText,
|
||||
responseData
|
||||
}))
|
||||
})
|
||||
)
|
||||
expect(store.getActions().length).toEqual(2)
|
||||
expect(store.getActions()).toEqual(expect.arrayContaining([
|
||||
expect(store.getActions()).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'OPTIONS_FORM_SUBMIT_START'
|
||||
}),
|
||||
|
@ -267,10 +290,11 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
success: false,
|
||||
message: expect.stringContaining("Couldn't save")
|
||||
})
|
||||
]))
|
||||
])
|
||||
)
|
||||
done()
|
||||
})
|
||||
.catch(e => done(e))
|
||||
.catch((e) => done(e))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -279,17 +303,20 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
beforeEach(() => {
|
||||
respondWith({
|
||||
url: `${apiUrl}/config`,
|
||||
method: 'PUT',
|
||||
method: 'POST',
|
||||
response: new XMLHttpRequest()
|
||||
})
|
||||
|
||||
reportRequestError.mockImplementation(() => MOCK_UI_MESSAGE)
|
||||
})
|
||||
|
||||
test('failed request is reported to console and failure with uiMessage is dispatched to store', done => {
|
||||
store.dispatch(submitPendingOptions()).then(() => {
|
||||
test('failed request is reported to console and failure with uiMessage is dispatched to store', (done) => {
|
||||
store
|
||||
.dispatch(submitPendingOptions())
|
||||
.then(() => {
|
||||
expect(reportRequestError).toHaveBeenCalledTimes(1)
|
||||
expect(reportRequestError).toHaveBeenCalledWith(expect.objectContaining({
|
||||
expect(reportRequestError).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
error: {
|
||||
errors: expect.objectContaining({
|
||||
fontawesome_request_noresponse: [expect.any(String)]
|
||||
|
@ -299,9 +326,11 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
request: expect.any(XMLHttpRequest)
|
||||
}
|
||||
}
|
||||
},
|
||||
}))
|
||||
expect(store.getActions()).toEqual(expect.arrayContaining([
|
||||
}
|
||||
})
|
||||
)
|
||||
expect(store.getActions()).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'OPTIONS_FORM_SUBMIT_START'
|
||||
}),
|
||||
|
@ -310,10 +339,11 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
success: false,
|
||||
message: MOCK_UI_MESSAGE
|
||||
})
|
||||
]))
|
||||
])
|
||||
)
|
||||
done()
|
||||
})
|
||||
.catch(e => done(e))
|
||||
.catch((e) => done(e))
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -321,17 +351,20 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
beforeEach(() => {
|
||||
respondWith({
|
||||
url: `${apiUrl}/config`,
|
||||
method: 'PUT',
|
||||
method: 'POST',
|
||||
response: new Error('some axios error')
|
||||
})
|
||||
|
||||
reportRequestError.mockImplementation(() => MOCK_UI_MESSAGE)
|
||||
})
|
||||
|
||||
test('failure is reported to console and failure with uiMessage is dispatched to store', done => {
|
||||
store.dispatch(submitPendingOptions()).then(() => {
|
||||
test('failure is reported to console and failure with uiMessage is dispatched to store', (done) => {
|
||||
store
|
||||
.dispatch(submitPendingOptions())
|
||||
.then(() => {
|
||||
expect(reportRequestError).toHaveBeenCalledTimes(1)
|
||||
expect(reportRequestError).toHaveBeenCalledWith(expect.objectContaining({
|
||||
expect(reportRequestError).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
error: {
|
||||
errors: expect.objectContaining({
|
||||
fontawesome_request_failed: [expect.stringContaining('server failed')]
|
||||
|
@ -341,9 +374,11 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
failedRequestMessage: 'some axios error'
|
||||
}
|
||||
}
|
||||
},
|
||||
}))
|
||||
expect(store.getActions()).toEqual(expect.arrayContaining([
|
||||
}
|
||||
})
|
||||
)
|
||||
expect(store.getActions()).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: 'OPTIONS_FORM_SUBMIT_START'
|
||||
}),
|
||||
|
@ -352,10 +387,11 @@ describe('submitPendingOptions and interceptors', () => {
|
|||
success: false,
|
||||
message: MOCK_UI_MESSAGE
|
||||
})
|
||||
]))
|
||||
])
|
||||
)
|
||||
done()
|
||||
})
|
||||
.catch(e => done(e))
|
||||
.catch((e) => done(e))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -364,7 +400,6 @@ describe('some action failure cases', () => {
|
|||
const STATE_TECH_CHANGE = {
|
||||
options: {
|
||||
technology: 'webfont'
|
||||
|
||||
},
|
||||
pendingOptions: {
|
||||
technology: 'svg'
|
||||
|
@ -389,7 +424,7 @@ describe('some action failure cases', () => {
|
|||
action: 'updateApiToken',
|
||||
state: {},
|
||||
route: 'config',
|
||||
method: 'PUT',
|
||||
method: 'POST',
|
||||
startAction: 'OPTIONS_FORM_SUBMIT_START',
|
||||
endAction: 'OPTIONS_FORM_SUBMIT_END',
|
||||
params: {
|
||||
|
@ -405,7 +440,7 @@ describe('some action failure cases', () => {
|
|||
}
|
||||
},
|
||||
route: 'conflict-detection/conflicts/blocklist',
|
||||
method: 'PUT',
|
||||
method: 'POST',
|
||||
startAction: 'BLOCKLIST_UPDATE_START',
|
||||
endAction: 'BLOCKLIST_UPDATE_END',
|
||||
params: {}
|
||||
|
@ -435,26 +470,17 @@ describe('some action failure cases', () => {
|
|||
params: {
|
||||
nodesTested: {
|
||||
conflict: {
|
||||
'abc123': {}
|
||||
abc123: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
action: 'snoozeV3DeprecationWarning',
|
||||
state: {},
|
||||
route: 'v3deprecation',
|
||||
method: 'PUT',
|
||||
startAction: 'SNOOZE_V3DEPRECATION_WARNING_START',
|
||||
endAction: 'SNOOZE_V3DEPRECATION_WARNING_END',
|
||||
params: {}
|
||||
},
|
||||
{
|
||||
action: 'setConflictDetectionScanner',
|
||||
desc: 'when enabling',
|
||||
state: {},
|
||||
route: 'conflict-detection/until',
|
||||
method: 'PUT',
|
||||
method: 'POST',
|
||||
startAction: 'ENABLE_CONFLICT_DETECTION_SCANNER_START',
|
||||
endAction: 'ENABLE_CONFLICT_DETECTION_SCANNER_END',
|
||||
params: { enable: true }
|
||||
|
@ -464,7 +490,7 @@ describe('some action failure cases', () => {
|
|||
desc: 'when disabling',
|
||||
state: {},
|
||||
route: 'conflict-detection/until',
|
||||
method: 'PUT',
|
||||
method: 'POST',
|
||||
startAction: 'DISABLE_CONFLICT_DETECTION_SCANNER_START',
|
||||
endAction: 'DISABLE_CONFLICT_DETECTION_SCANNER_END',
|
||||
params: { enable: false }
|
||||
|
@ -482,7 +508,7 @@ describe('some action failure cases', () => {
|
|||
action: 'submitPendingOptions',
|
||||
state: STATE_TECH_CHANGE,
|
||||
route: 'config',
|
||||
method: 'PUT',
|
||||
method: 'POST',
|
||||
startAction: 'OPTIONS_FORM_SUBMIT_START',
|
||||
endAction: 'OPTIONS_FORM_SUBMIT_END',
|
||||
params: undefined
|
||||
|
@ -493,11 +519,11 @@ describe('some action failure cases', () => {
|
|||
|
||||
const data = {
|
||||
errors: {
|
||||
"code1": ["message1"],
|
||||
code1: ['message1']
|
||||
},
|
||||
error_data: {
|
||||
"code1": {
|
||||
"trace": 'some stack trace'
|
||||
code1: {
|
||||
trace: 'some stack trace'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -510,7 +536,7 @@ describe('some action failure cases', () => {
|
|||
resetAxiosMocks()
|
||||
})
|
||||
|
||||
cases.map(c => {
|
||||
cases.map((c) => {
|
||||
describe(`${c.action}${c.desc || ''}`, () => {
|
||||
let store = null
|
||||
|
||||
|
@ -530,26 +556,31 @@ describe('some action failure cases', () => {
|
|||
response: {
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
data: `${garbage}${JSON.stringify(data)}`,
|
||||
data: `${garbage}${JSON.stringify(data)}`
|
||||
// no confirmation header
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test('reports warning and dispatches a failure action despite the garbage', done => {
|
||||
store.dispatch(actions[c.action](c.params)).then(() => {
|
||||
test('reports warning and dispatches a failure action despite the garbage', (done) => {
|
||||
store
|
||||
.dispatch(actions[c.action](c.params))
|
||||
.then(() => {
|
||||
expect(reportRequestError).toHaveBeenCalledTimes(1)
|
||||
expect(reportRequestError).toHaveBeenCalledWith(expect.objectContaining({
|
||||
expect(reportRequestError).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
error: expect.objectContaining({
|
||||
errors: expect.anything(),
|
||||
'error_data': expect.anything()
|
||||
error_data: expect.anything()
|
||||
}),
|
||||
confirmed: false,
|
||||
falsePositive: true,
|
||||
trimmed: garbage
|
||||
}))
|
||||
})
|
||||
)
|
||||
expect(store.getActions().length).toEqual(2)
|
||||
expect(store.getActions()).toEqual(expect.arrayContaining([
|
||||
expect(store.getActions()).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: c.startAction
|
||||
}),
|
||||
|
@ -558,10 +589,11 @@ describe('some action failure cases', () => {
|
|||
success: false,
|
||||
message: expect.stringMatching(/[a-z]/)
|
||||
})
|
||||
]))
|
||||
])
|
||||
)
|
||||
done()
|
||||
})
|
||||
.catch(e => done(e))
|
||||
.catch((e) => done(e))
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -581,20 +613,25 @@ describe('some action failure cases', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test('reports ui and console error messages', done => {
|
||||
test('reports ui and console error messages', (done) => {
|
||||
reportRequestError.mockReturnValueOnce(null)
|
||||
store.dispatch(actions[c.action](c.params)).then(() => {
|
||||
store
|
||||
.dispatch(actions[c.action](c.params))
|
||||
.then(() => {
|
||||
expect(reportRequestError).toHaveBeenCalledTimes(1)
|
||||
expect(reportRequestError).toHaveBeenCalledWith(expect.objectContaining({
|
||||
expect(reportRequestError).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
error: expect.objectContaining({
|
||||
errors: expect.anything(),
|
||||
'error_data': expect.anything()
|
||||
error_data: expect.anything()
|
||||
}),
|
||||
confirmed: true,
|
||||
trimmed: ''
|
||||
}))
|
||||
})
|
||||
)
|
||||
expect(store.getActions().length).toEqual(2)
|
||||
expect(store.getActions()).toEqual(expect.arrayContaining([
|
||||
expect(store.getActions()).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
type: c.startAction
|
||||
}),
|
||||
|
@ -603,10 +640,11 @@ describe('some action failure cases', () => {
|
|||
success: false,
|
||||
message: expect.stringMatching(/[a-z]/)
|
||||
})
|
||||
]))
|
||||
])
|
||||
)
|
||||
done()
|
||||
})
|
||||
.catch(e => done(e))
|
||||
.catch((e) => done(e))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -653,10 +691,6 @@ describe('reportDetectedConflicts', () => {
|
|||
test.todo('success')
|
||||
})
|
||||
|
||||
describe('snoozeV3DeprecationWarning', () => {
|
||||
test.todo('success')
|
||||
})
|
||||
|
||||
describe('setConflictDetectionScanner', () => {
|
||||
test.todo('success when enabling')
|
||||
test.todo('success when disabling')
|
||||
|
@ -739,7 +773,8 @@ describe('preprocessResponse', () => {
|
|||
|
||||
actions.preprocessResponse(response)
|
||||
|
||||
expect(reportRequestError).toHaveBeenCalledWith(expect.objectContaining({
|
||||
expect(reportRequestError).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
confirmed: false,
|
||||
requestData,
|
||||
requestMethod: method,
|
||||
|
@ -749,7 +784,8 @@ describe('preprocessResponse', () => {
|
|||
requestData: REDACTED_REQUEST_DATA,
|
||||
responseHeaders: REDACTED_HEADERS,
|
||||
requestHeaders: REDACTED_HEADERS
|
||||
}))
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -780,7 +816,8 @@ describe('preprocessResponse', () => {
|
|||
|
||||
actions.preprocessResponse(response)
|
||||
|
||||
expect(reportRequestError).toHaveBeenCalledWith(expect.objectContaining({
|
||||
expect(reportRequestError).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
confirmed: false,
|
||||
requestMethod: method,
|
||||
requestUrl: url,
|
||||
|
@ -790,7 +827,8 @@ describe('preprocessResponse', () => {
|
|||
requestData: REDACTED_REQUEST_DATA,
|
||||
responseHeaders: REDACTED_HEADERS,
|
||||
requestHeaders: REDACTED_HEADERS
|
||||
}))
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,22 +1,13 @@
|
|||
import { createStore as reduxCreateStore, applyMiddleware, compose } from 'redux'
|
||||
import thunkMiddleware from 'redux-thunk'
|
||||
import { thunk } from 'redux-thunk'
|
||||
import rootReducer from './reducers'
|
||||
|
||||
const middleware = [ thunkMiddleware ]
|
||||
const middleware = [thunk]
|
||||
|
||||
const composeEnhancers = (
|
||||
process.env.NODE_ENV === 'development'
|
||||
&& window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
|
||||
) || compose
|
||||
const composeEnhancers = (process.env.NODE_ENV === 'development' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose
|
||||
|
||||
const enhancer = composeEnhancers(
|
||||
applyMiddleware(...middleware)
|
||||
)
|
||||
const enhancer = composeEnhancers(applyMiddleware(...middleware))
|
||||
|
||||
export function createStore(initialData = {}) {
|
||||
return reduxCreateStore(
|
||||
rootReducer,
|
||||
initialData,
|
||||
enhancer
|
||||
)
|
||||
return reduxCreateStore(rootReducer, initialData, enhancer)
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
import size from 'lodash/size'
|
||||
import omit from 'lodash/omit'
|
||||
import get from 'lodash/get'
|
||||
import { size, omit, get } from 'lodash'
|
||||
import { combineReducers } from 'redux'
|
||||
|
||||
export const ADMIN_TAB_SETTINGS = 'ADMIN_TAB_SETTINGS'
|
||||
export const ADMIN_TAB_TROUBLESHOOT = 'ADMIN_TAB_TROUBLESHOOT'
|
||||
|
||||
const coerceBool = val => val === true || val === "1"
|
||||
const coerceBool = (val) => val === true || val === '1'
|
||||
|
||||
const coerceEmptyArrayToEmptyObject = val => size(val) === 0 ? {} : val
|
||||
const coerceEmptyArrayToEmptyObject = (val) => (size(val) === 0 ? {} : val)
|
||||
|
||||
// TODO: add reducer for the clientPreferences that coerces their boolean options
|
||||
|
||||
|
@ -32,15 +30,7 @@ export function options(state = {}, action = {}) {
|
|||
return state
|
||||
} else {
|
||||
const {
|
||||
options: {
|
||||
technology,
|
||||
usePro,
|
||||
compat,
|
||||
pseudoElements,
|
||||
version,
|
||||
kitToken,
|
||||
apiToken
|
||||
}
|
||||
options: { technology, usePro, compat, pseudoElements, version, kitToken, apiToken }
|
||||
} = data
|
||||
|
||||
return {
|
||||
|
@ -65,8 +55,7 @@ const OPTIONS_FORM_INITIAL_STATE = {
|
|||
message: ''
|
||||
}
|
||||
|
||||
function optionsFormState(
|
||||
state = OPTIONS_FORM_INITIAL_STATE, action = {}) {
|
||||
function optionsFormState(state = OPTIONS_FORM_INITIAL_STATE, action = {}) {
|
||||
const { type, success, message } = action
|
||||
|
||||
switch (type) {
|
||||
|
@ -128,9 +117,7 @@ const INITIAL_STATE_UNREGISTERED_CLIENTS_DELETION_STATUS = {
|
|||
message: ''
|
||||
}
|
||||
|
||||
function unregisteredClientsDeletionStatus(
|
||||
state = INITIAL_STATE_UNREGISTERED_CLIENTS_DELETION_STATUS,
|
||||
action = {} ) {
|
||||
function unregisteredClientsDeletionStatus(state = INITIAL_STATE_UNREGISTERED_CLIENTS_DELETION_STATUS, action = {}) {
|
||||
const { type, success, message } = action
|
||||
|
||||
switch (type) {
|
||||
|
@ -222,7 +209,8 @@ function kitsQueryStatus(
|
|||
isSubmitting: false,
|
||||
message: ''
|
||||
},
|
||||
action = {}) {
|
||||
action = {}
|
||||
) {
|
||||
const { type, success, message } = action
|
||||
|
||||
switch (type) {
|
||||
|
@ -327,8 +315,8 @@ function unregisteredClientDetectionStatus(
|
|||
recentConflictsDetected: {},
|
||||
message: ''
|
||||
},
|
||||
action = {}) {
|
||||
|
||||
action = {}
|
||||
) {
|
||||
const { type, success, message, unregisteredClientsBeforeDetection, recentConflictsDetected } = action
|
||||
|
||||
switch (type) {
|
||||
|
@ -358,8 +346,8 @@ function conflictDetectionScannerStatus(
|
|||
success: false,
|
||||
message: ''
|
||||
},
|
||||
action = {}) {
|
||||
|
||||
action = {}
|
||||
) {
|
||||
const { type, success, message } = action
|
||||
|
||||
switch (type) {
|
||||
|
@ -374,37 +362,6 @@ function conflictDetectionScannerStatus(
|
|||
}
|
||||
}
|
||||
|
||||
function v3DeprecationWarningStatus(
|
||||
state = {
|
||||
isSubmitting: false,
|
||||
hasSubmitted: false,
|
||||
success: false,
|
||||
message: ''
|
||||
},
|
||||
action = {}) {
|
||||
const { type, success, message } = action
|
||||
|
||||
switch(type) {
|
||||
case 'SNOOZE_V3DEPRECATION_WARNING_START':
|
||||
return { ...state, isSubmitting: true, hasSubmitted: true }
|
||||
case 'SNOOZE_V3DEPRECATION_WARNING_END':
|
||||
return { ...state, isSubmitting: false, success, message }
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
function v3DeprecationWarning(state = {}, action = {}) {
|
||||
const { type, snooze = false } = action
|
||||
|
||||
switch(type) {
|
||||
case 'SNOOZE_V3DEPRECATION_WARNING_END':
|
||||
return { ...state, snooze }
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
function showConflictDetectionReporter(state = false, action = {}) {
|
||||
const { type } = action
|
||||
|
||||
|
@ -448,12 +405,15 @@ function activeAdminTab(state = ADMIN_TAB_SETTINGS, action = {}) {
|
|||
}
|
||||
}
|
||||
|
||||
function simple(state = {}, _action) { return state }
|
||||
function simple(state = {}, _action) {
|
||||
return state
|
||||
}
|
||||
|
||||
export default combineReducers({
|
||||
activeAdminTab,
|
||||
apiNonce: simple,
|
||||
apiUrl: simple,
|
||||
faApiUrl: simple,
|
||||
blocklistUpdateStatus,
|
||||
clientPreferences: coerceEmptyArrayToEmptyObject,
|
||||
conflictDetectionScannerStatus,
|
||||
|
@ -472,7 +432,6 @@ export default combineReducers({
|
|||
rootUrl: simple,
|
||||
mainCdnAssetUrl: simple,
|
||||
mainCdnAssetIntegrity: simple,
|
||||
enableIconChooser: coerceBool,
|
||||
releases: simple,
|
||||
settingsPageUrl: simple,
|
||||
showAdmin: coerceBool,
|
||||
|
@ -481,9 +440,5 @@ export default combineReducers({
|
|||
unregisteredClients,
|
||||
unregisteredClientsDeletionStatus,
|
||||
userAttemptedToStopScanner,
|
||||
v3DeprecationWarning,
|
||||
v3DeprecationWarningStatus,
|
||||
webpackPublicPath: simple,
|
||||
isGutenbergPage: coerceBool,
|
||||
usingCompatJs: coerceBool
|
||||
webpackPublicPath: simple
|
||||
})
|
||||
|
|
|
@ -21,7 +21,7 @@ describe('options', () => {
|
|||
usePro: true,
|
||||
compat: false,
|
||||
pseudoElements: true,
|
||||
version: '5.11.2',
|
||||
version: '5.11.2'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,11 +6,11 @@ export async function resetOptions(page) {
|
|||
options: {
|
||||
usePro: false,
|
||||
compat: true,
|
||||
technology:"webfont",
|
||||
technology: 'webfont',
|
||||
pseudoElements: true,
|
||||
kitToken: null,
|
||||
apiToken: true,
|
||||
version:"6.0.0-beta3"
|
||||
version: '6.0.0-beta3'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,40 @@
|
|||
import get from 'lodash/get'
|
||||
import set from 'lodash/set'
|
||||
import size from 'lodash/size'
|
||||
import { get, set, size } from 'lodash'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
|
||||
export const ERROR_REPORT_PREAMBLE = __('Font Awesome WordPress Plugin Error Report', 'font-awesome')
|
||||
const UI_MESSAGE_DEFAULT = __( 'D\'oh! That failed big time.', 'font-awesome' )
|
||||
const UI_MESSAGE_DEFAULT = __("D'oh! That failed big time.", 'font-awesome')
|
||||
const ERROR_REPORTING_ERROR = __('There was an error attempting to report the error.', 'font-awesome')
|
||||
const REST_NO_ROUTE_ERROR = __('Oh no! Your web browser could not reach your WordPress server.', 'font-awesome')
|
||||
const REST_COOKIE_INVALID_NONCE_ERROR = __( 'It looks like your web browser session expired. Try logging out and log back in to WordPress admin.', 'font-awesome' )
|
||||
const OK_ERROR_PREAMBLE = __( 'The last request was successful, but it also returned the following error(s), which might be helpful for troubleshooting.', 'font-awesome' )
|
||||
const REST_COOKIE_INVALID_NONCE_ERROR = __(
|
||||
'It looks like your web browser session expired. Try logging out and log back in to WordPress admin.',
|
||||
'font-awesome'
|
||||
)
|
||||
const OK_ERROR_PREAMBLE = __(
|
||||
'The last request was successful, but it also returned the following error(s), which might be helpful for troubleshooting.',
|
||||
'font-awesome'
|
||||
)
|
||||
const ONE_OF_MANY_ERRORS_GROUP_LABEL = __('Error', 'font-awesome')
|
||||
const FALSE_POSITIVE_MESSAGE = __( 'WARNING: The last request contained errors, though your WordPress server reported it as a success. This usually means there\'s a problem with your theme or one of your other plugins emitting output that is causing problems.', 'font-awesome' )
|
||||
const UNCONFIRMED_RESPONSE_MESSAGE = __( 'WARNING: The last response from your WordPress server did not include the confirmation header that should be in all valid Font Awesome responses. This is a clue that some code from another theme or plugin is acting badly and causing the wrong headers to be sent.', 'font-awesome')
|
||||
const CONFIRMED_RESPONSE_MESSAGE = __( 'CONFIRMED: The last response from your WordPress server included the confirmation header that is expected for all valid responses from the Font Awesome plugin\'s code running on your WordPress server.', 'font-awesome')
|
||||
const FALSE_POSITIVE_MESSAGE = __(
|
||||
"WARNING: The last request contained errors, though your WordPress server reported it as a success. This usually means there's a problem with your theme or one of your other plugins emitting output that is causing problems.",
|
||||
'font-awesome'
|
||||
)
|
||||
const UNCONFIRMED_RESPONSE_MESSAGE = __(
|
||||
'WARNING: The last response from your WordPress server did not include the confirmation header that should be in all valid Font Awesome responses. This is a clue that some code from another theme or plugin is acting badly and causing the wrong headers to be sent.',
|
||||
'font-awesome'
|
||||
)
|
||||
const CONFIRMED_RESPONSE_MESSAGE = __(
|
||||
"CONFIRMED: The last response from your WordPress server included the confirmation header that is expected for all valid responses from the Font Awesome plugin's code running on your WordPress server.",
|
||||
'font-awesome'
|
||||
)
|
||||
const TRIMMED_RESPONSE_PREAMBLE = __('WARNING: Invalid Data Trimmed from Server Response', 'font-awesome')
|
||||
const EXPECTED_EMPTY_MESSAGE = __( 'WARNING: We expected the last response from the server to contain no data, but it contained something unexpected.', 'font-awesome' )
|
||||
const MISSING_ERROR_DATA_MESSAGE = __( 'Your WordPress server returned an error for that last request, but there was no information about the error.', 'font-awesome' )
|
||||
const EXPECTED_EMPTY_MESSAGE = __(
|
||||
'WARNING: We expected the last response from the server to contain no data, but it contained something unexpected.',
|
||||
'font-awesome'
|
||||
)
|
||||
const MISSING_ERROR_DATA_MESSAGE = __(
|
||||
'Your WordPress server returned an error for that last request, but there was no information about the error.',
|
||||
'font-awesome'
|
||||
)
|
||||
const REPORT_INFO_PARAM_KEYS = [
|
||||
'requestMethod',
|
||||
'responseStatus',
|
||||
|
@ -97,7 +116,7 @@ function handleSingleWpErrorOutput( wpError ) {
|
|||
}
|
||||
|
||||
function handleAllWpErrorOutput(errorData = {}) {
|
||||
const wpErrors = Object.keys(errorData.errors || []).map(code => {
|
||||
const wpErrors = Object.keys(errorData.errors || []).map((code) => {
|
||||
// get the first error message available for this code
|
||||
const message = get(errorData, `errors.${code}.0`)
|
||||
const data = get(errorData, `error_data.${code}`)
|
||||
|
@ -125,23 +144,14 @@ function handleAllWpErrorOutput(errorData = {}) {
|
|||
|
||||
// The uiMessage we should return will be the first error message that isn't
|
||||
// from a 'previous_exception'
|
||||
return (!acc && error.code !== 'previous_exception')
|
||||
? msg
|
||||
: acc
|
||||
return !acc && error.code !== 'previous_exception' ? msg : acc
|
||||
}, null)
|
||||
|
||||
return uiMessage
|
||||
}
|
||||
|
||||
function report(params) {
|
||||
const {
|
||||
error = null,
|
||||
ok = false,
|
||||
falsePositive = false,
|
||||
confirmed = false,
|
||||
expectEmpty = false,
|
||||
trimmed = ''
|
||||
} = params
|
||||
const { error = null, ok = false, falsePositive = false, confirmed = false, expectEmpty = false, trimmed = '' } = params
|
||||
|
||||
console.group(ERROR_REPORT_PREAMBLE)
|
||||
|
||||
|
@ -195,9 +205,7 @@ function report(params) {
|
|||
console.groupEnd()
|
||||
}
|
||||
|
||||
const uiMessage = null !== error
|
||||
? handleAllWpErrorOutput( error )
|
||||
: null
|
||||
const uiMessage = null !== error ? handleAllWpErrorOutput(error) : null
|
||||
|
||||
if (error && trimmed === '' && confirmed) {
|
||||
console.info(MISSING_ERROR_DATA_MESSAGE)
|
||||
|
|
|
@ -6,12 +6,13 @@ console.info = jest.fn()
|
|||
|
||||
const SINGLE_EXCEPTION_ERROR = {
|
||||
errors: {
|
||||
fontawesome_client_exception: ["Whoops, it looks like that API Token is not valid. Try another one?"]
|
||||
fontawesome_client_exception: ['Whoops, it looks like that API Token is not valid. Try another one?']
|
||||
},
|
||||
error_data: {
|
||||
fontawesome_client_exception: {
|
||||
status: 400,
|
||||
trace:"#0 \/var\/www\/html\/wp-content\/plugins\/font-awesome\/includes\/class-fontawesome-api-settings.php(311): FortAwesome\\FontAwesome_Exception::with_wp_response(Array)\n#1 \/var\/www\/html\/wp-content\/plugins\/font-awesome\/includes\/class-fontawesome-config-controller.php(115): FortAwesome\\FontAwesome_API_Settings->request_access_token()\n#2 \/var\/www\/html\/wp-includes\/rest-api\/class-wp-rest-server.php(946): FortAwesome\\FontAwesome_Config_Controller->update_item(Object(WP_REST_Request))\n#3 \/var\/www\/html\/wp-includes\/rest-api\/class-wp-rest-server.php(329): WP_REST_Server->dispatch(Object(WP_REST_Request))\n#4 \/var\/www\/html\/wp-includes\/rest-api.php(305): WP_REST_Server->serve_request('\/font-awesome\/v...')\n#5 \/var\/www\/html\/wp-includes\/class-wp-hook.php(288): rest_api_loaded(Object(WP))\n#6 \/var\/www\/html\/wp-includes\/class-wp-hook.php(312): WP_Hook->apply_filters('', Array)\n#7 \/var\/www\/html\/wp-includes\/plugin.php(544): WP_Hook->do_action(Array)\n#8 \/var\/www\/html\/wp-includes\/class-wp.php(387): do_action_ref_array('parse_request', Array)\n#9 \/var\/www\/html\/wp-includes\/class-wp.php(729): WP->parse_request('')\n#10 \/var\/www\/html\/wp-includes\/functions.php(1255): WP->main('')\n#11 \/var\/www\/html\/wp-blog-header.php(16): wp()\n#12 \/var\/www\/html\/index.php(17): require('\/var\/www\/html\/w...')\n#13 {main}"
|
||||
trace:
|
||||
"#0 /var/www/html/wp-content/plugins/font-awesome/includes/class-fontawesome-api-settings.php(311): FortAwesome\\FontAwesome_Exception::with_wp_response(Array)\n#1 /var/www/html/wp-content/plugins/font-awesome/includes/class-fontawesome-config-controller.php(115): FortAwesome\\FontAwesome_API_Settings->request_access_token()\n#2 /var/www/html/wp-includes/rest-api/class-wp-rest-server.php(946): FortAwesome\\FontAwesome_Config_Controller->update_item(Object(WP_REST_Request))\n#3 /var/www/html/wp-includes/rest-api/class-wp-rest-server.php(329): WP_REST_Server->dispatch(Object(WP_REST_Request))\n#4 /var/www/html/wp-includes/rest-api.php(305): WP_REST_Server->serve_request('/font-awesome/v...')\n#5 /var/www/html/wp-includes/class-wp-hook.php(288): rest_api_loaded(Object(WP))\n#6 /var/www/html/wp-includes/class-wp-hook.php(312): WP_Hook->apply_filters('', Array)\n#7 /var/www/html/wp-includes/plugin.php(544): WP_Hook->do_action(Array)\n#8 /var/www/html/wp-includes/class-wp.php(387): do_action_ref_array('parse_request', Array)\n#9 /var/www/html/wp-includes/class-wp.php(729): WP->parse_request('')\n#10 /var/www/html/wp-includes/functions.php(1255): WP->main('')\n#11 /var/www/html/wp-blog-header.php(16): wp()\n#12 /var/www/html/index.php(17): require('/var/www/html/w...')\n#13 {main}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +33,6 @@ describe('reportRequestError', () => {
|
|||
})
|
||||
|
||||
describe('with single fontawesome_client_exception', () => {
|
||||
|
||||
test('emits console report and returns uiMessage from given error', () => {
|
||||
const message = reportRequestError({ error: SINGLE_EXCEPTION_ERROR })
|
||||
|
||||
|
@ -41,20 +41,12 @@ describe('reportRequestError', () => {
|
|||
expect(console.group).toHaveBeenCalledTimes(2)
|
||||
expect(console.info).toHaveBeenCalled()
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/message: Whoops/),
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/message: Whoops/))
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/trace:/)
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/trace:/))
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/status:/),
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/code: fontawesome_client_exception/)
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/status:/))
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/code: fontawesome_client_exception/))
|
||||
|
||||
expect(console.groupEnd).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
@ -63,17 +55,19 @@ describe('reportRequestError', () => {
|
|||
describe('when PreferenceRegistrationException is thrown with a previous exception', () => {
|
||||
const error = {
|
||||
errors: {
|
||||
fontawesome_server_exception: ["A theme or plugin registered with Font Awesome threw an exception."],
|
||||
previous_exception: ["epsilon-plugin throwing"]
|
||||
fontawesome_server_exception: ['A theme or plugin registered with Font Awesome threw an exception.'],
|
||||
previous_exception: ['epsilon-plugin throwing']
|
||||
},
|
||||
error_data: {
|
||||
fontawesome_server_exception: {
|
||||
status: 500,
|
||||
trace:"#0 \/var\/www\/html\/wp-content\/plugins\/font-awesome\/includes\/class-fontawesome.php(1057): FortAwesome\\FontAwesome_Exception::with_thrown(Object(Exception))\n#1 \/var\/www\/html\/wp-content\/plugins\/font-awesome\/includes\/class-fontawesome-config-controller.php(80): FortAwesome\\FontAwesome->gather_preferences()\n#2 \/var\/www\/html\/wp-content\/plugins\/font-awesome\/includes\/class-fontawesome-config-controller.php(134): FortAwesome\\FontAwesome_Config_Controller->build_item(Object(FortAwesome\\FontAwesome))\n#3 \/var\/www\/html\/wp-includes\/rest-api\/class-wp-rest-server.php(946): FortAwesome\\FontAwesome_Config_Controller->update_item(Object(WP_REST_Request))\n#4 \/var\/www\/html\/wp-includes\/rest-api\/class-wp-rest-server.php(329): WP_REST_Server->dispatch(Object(WP_REST_Request))\n#5 \/var\/www\/html\/wp-includes\/rest-api.php(305): WP_REST_Server->serve_request('\/font-awesome\/v...')\n#6 \/var\/www\/html\/wp-includes\/class-wp-hook.php(288): rest_api_loaded(Object(WP))\n#7 \/var\/www\/html\/wp-includes\/class-wp-hook.php(312): WP_Hook->apply_filters('', Array)\n#8 \/var\/www\/html\/wp-includes\/plugin.php(544): WP_Hook->do_action(Array)\n#9 \/var\/www\/html\/wp-includes\/class-wp.php(387): do_action_ref_array('parse_request', Array)\n#10 \/var\/www\/html\/wp-includes\/class-wp.php(729): WP->parse_request('')\n#11 \/var\/www\/html\/wp-includes\/functions.php(1255): WP->main('')\n#12 \/var\/www\/html\/wp-blog-header.php(16): wp()\n#13 \/var\/www\/html\/index.php(17): require('\/var\/www\/html\/w...')\n#14 {main}"
|
||||
trace:
|
||||
"#0 /var/www/html/wp-content/plugins/font-awesome/includes/class-fontawesome.php(1057): FortAwesome\\FontAwesome_Exception::with_thrown(Object(Exception))\n#1 /var/www/html/wp-content/plugins/font-awesome/includes/class-fontawesome-config-controller.php(80): FortAwesome\\FontAwesome->gather_preferences()\n#2 /var/www/html/wp-content/plugins/font-awesome/includes/class-fontawesome-config-controller.php(134): FortAwesome\\FontAwesome_Config_Controller->build_item(Object(FortAwesome\\FontAwesome))\n#3 /var/www/html/wp-includes/rest-api/class-wp-rest-server.php(946): FortAwesome\\FontAwesome_Config_Controller->update_item(Object(WP_REST_Request))\n#4 /var/www/html/wp-includes/rest-api/class-wp-rest-server.php(329): WP_REST_Server->dispatch(Object(WP_REST_Request))\n#5 /var/www/html/wp-includes/rest-api.php(305): WP_REST_Server->serve_request('/font-awesome/v...')\n#6 /var/www/html/wp-includes/class-wp-hook.php(288): rest_api_loaded(Object(WP))\n#7 /var/www/html/wp-includes/class-wp-hook.php(312): WP_Hook->apply_filters('', Array)\n#8 /var/www/html/wp-includes/plugin.php(544): WP_Hook->do_action(Array)\n#9 /var/www/html/wp-includes/class-wp.php(387): do_action_ref_array('parse_request', Array)\n#10 /var/www/html/wp-includes/class-wp.php(729): WP->parse_request('')\n#11 /var/www/html/wp-includes/functions.php(1255): WP->main('')\n#12 /var/www/html/wp-blog-header.php(16): wp()\n#13 /var/www/html/index.php(17): require('/var/www/html/w...')\n#14 {main}"
|
||||
},
|
||||
previous_exception: {
|
||||
status: 500,
|
||||
trace: "#0 \/var\/www\/html\/wp-includes\/class-wp-hook.php(288): {closure}('')\n#1 \/var\/www\/html\/wp-includes\/class-wp-hook.php(312): WP_Hook->apply_filters('', Array)\n#2 \/var\/www\/html\/wp-includes\/plugin.php(478): WP_Hook->do_action(Array)\n#3 \/var\/www\/html\/wp-content\/plugins\/font-awesome\/includes\/class-fontawesome.php(1055): do_action('font_awesome_pr...')\n#4 \/var\/www\/html\/wp-content\/plugins\/font-awesome\/includes\/class-fontawesome-config-controller.php(80): FortAwesome\\FontAwesome->gather_preferences()\n#5 \/var\/www\/html\/wp-content\/plugins\/font-awesome\/includes\/class-fontawesome-config-controller.php(134): FortAwesome\\FontAwesome_Config_Controller->build_item(Object(FortAwesome\\FontAwesome))\n#6 \/var\/www\/html\/wp-includes\/rest-api\/class-wp-rest-server.php(946): FortAwesome\\FontAwesome_Config_Controller->update_item(Object(WP_REST_Request))\n#7 \/var\/www\/html\/wp-includes\/rest-api\/class-wp-rest-server.php(329): WP_REST_Server->dispatch(Object(WP_REST_Request))\n#8 \/var\/www\/html\/wp-includes\/rest-api.php(305): WP_REST_Server->serve_request('\/font-awesome\/v...')\n#9 \/var\/www\/html\/wp-includes\/class-wp-hook.php(288): rest_api_loaded(Object(WP))\n#10 \/var\/www\/html\/wp-includes\/class-wp-hook.php(312): WP_Hook->apply_filters('', Array)\n#11 \/var\/www\/html\/wp-includes\/plugin.php(544): WP_Hook->do_action(Array)\n#12 \/var\/www\/html\/wp-includes\/class-wp.php(387): do_action_ref_array('parse_request', Array)\n#13 \/var\/www\/html\/wp-includes\/class-wp.php(729): WP->parse_request('')\n#14 \/var\/www\/html\/wp-includes\/functions.php(1255): WP->main('')\n#15 \/var\/www\/html\/wp-blog-header.php(16): wp()\n#16 \/var\/www\/html\/index.php(17): require('\/var\/www\/html\/w...')\n#17 {main}"
|
||||
trace:
|
||||
"#0 /var/www/html/wp-includes/class-wp-hook.php(288): {closure}('')\n#1 /var/www/html/wp-includes/class-wp-hook.php(312): WP_Hook->apply_filters('', Array)\n#2 /var/www/html/wp-includes/plugin.php(478): WP_Hook->do_action(Array)\n#3 /var/www/html/wp-content/plugins/font-awesome/includes/class-fontawesome.php(1055): do_action('font_awesome_pr...')\n#4 /var/www/html/wp-content/plugins/font-awesome/includes/class-fontawesome-config-controller.php(80): FortAwesome\\FontAwesome->gather_preferences()\n#5 /var/www/html/wp-content/plugins/font-awesome/includes/class-fontawesome-config-controller.php(134): FortAwesome\\FontAwesome_Config_Controller->build_item(Object(FortAwesome\\FontAwesome))\n#6 /var/www/html/wp-includes/rest-api/class-wp-rest-server.php(946): FortAwesome\\FontAwesome_Config_Controller->update_item(Object(WP_REST_Request))\n#7 /var/www/html/wp-includes/rest-api/class-wp-rest-server.php(329): WP_REST_Server->dispatch(Object(WP_REST_Request))\n#8 /var/www/html/wp-includes/rest-api.php(305): WP_REST_Server->serve_request('/font-awesome/v...')\n#9 /var/www/html/wp-includes/class-wp-hook.php(288): rest_api_loaded(Object(WP))\n#10 /var/www/html/wp-includes/class-wp-hook.php(312): WP_Hook->apply_filters('', Array)\n#11 /var/www/html/wp-includes/plugin.php(544): WP_Hook->do_action(Array)\n#12 /var/www/html/wp-includes/class-wp.php(387): do_action_ref_array('parse_request', Array)\n#13 /var/www/html/wp-includes/class-wp.php(729): WP->parse_request('')\n#14 /var/www/html/wp-includes/functions.php(1255): WP->main('')\n#15 /var/www/html/wp-blog-header.php(16): wp()\n#16 /var/www/html/index.php(17): require('/var/www/html/w...')\n#17 {main}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,17 +78,11 @@ describe('reportRequestError', () => {
|
|||
expect(console.group).toHaveBeenCalledTimes(3)
|
||||
expect(console.groupEnd).toHaveBeenCalledTimes(3)
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/code: fontawesome_server_exception/)
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/code: fontawesome_server_exception/))
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/code: previous_exception/)
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/code: previous_exception/))
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/The last request was successful/)
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/The last request was successful/))
|
||||
|
||||
expect(message).toMatch(/^A theme or plugin/)
|
||||
})
|
||||
|
@ -114,48 +102,28 @@ describe('reportRequestError', () => {
|
|||
expect(message).toMatch(/^Whoops/)
|
||||
// The top-level group, and then one error group, then one for the trimmed content
|
||||
expect(console.group).toHaveBeenCalledTimes(3)
|
||||
expect(console.group).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/Error Report/),
|
||||
)
|
||||
expect(console.group).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/Trimmed/),
|
||||
)
|
||||
expect(console.group).toHaveBeenCalledWith(expect.stringMatching(/Error Report/))
|
||||
expect(console.group).toHaveBeenCalledWith(expect.stringMatching(/Trimmed/))
|
||||
|
||||
expect(console.groupEnd).toHaveBeenCalledTimes(3)
|
||||
|
||||
expect(console.info).toHaveBeenCalledTimes(4)
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/reported it as a success/),
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/reported it as a success/))
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/This is a clue/),
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/This is a clue/))
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/message: Whoops/),
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/message: Whoops/))
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/message: Whoops/),
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/message: Whoops/))
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/trace:/)
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/trace:/))
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/status:/),
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/status:/))
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/code: fontawesome_client_exception/)
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/code: fontawesome_client_exception/))
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringContaining(TRIMMED)
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringContaining(TRIMMED))
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -172,21 +140,15 @@ describe('reportRequestError', () => {
|
|||
expect(message).toBeNull()
|
||||
// The top-level group, and then one for the trimmed content
|
||||
expect(console.group).toHaveBeenCalledTimes(2)
|
||||
expect(console.group).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/Trimmed/),
|
||||
)
|
||||
expect(console.group).toHaveBeenCalledWith(expect.stringMatching(/Trimmed/))
|
||||
|
||||
expect(console.groupEnd).toHaveBeenCalledTimes(2)
|
||||
|
||||
expect(console.info).toHaveBeenCalled()
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/contain no data/),
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/contain no data/))
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringContaining(TRIMMED),
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringContaining(TRIMMED))
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -203,9 +165,7 @@ describe('reportRequestError', () => {
|
|||
|
||||
expect(console.info).toHaveBeenCalledTimes(1)
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/did not include the confirmation header/),
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/did not include the confirmation header/))
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -257,9 +217,7 @@ describe('reportRequestError', () => {
|
|||
|
||||
expect(console.info).toHaveBeenCalled()
|
||||
|
||||
expect(console.info).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/failure console message/)
|
||||
)
|
||||
expect(console.info).toHaveBeenCalledWith(expect.stringMatching(/failure console message/))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -273,7 +231,7 @@ describe('redactRequestData', () => {
|
|||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify({ options: { foo: 42, apiToken: 'abc123' } })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
expect(redactRequestData(response)).toEqual(JSON.stringify({ options: { foo: 42, apiToken: 'REDACTED' } }))
|
||||
|
@ -288,7 +246,7 @@ describe('redactRequestData', () => {
|
|||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify({ options: { foo: 42, apiToken: true } })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
expect(redactRequestData(response)).toEqual(JSON.stringify({ options: { foo: 42, apiToken: true } }))
|
||||
|
@ -303,7 +261,7 @@ describe('redactRequestData', () => {
|
|||
'Content-Type': 'text/plain'
|
||||
},
|
||||
data: JSON.stringify({ options: { foo: 42, beta: 43 } })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
expect(redactRequestData(response)).toEqual(JSON.stringify({ options: { foo: 42, beta: 43 } }))
|
||||
|
@ -313,14 +271,20 @@ describe('redactRequestData', () => {
|
|||
|
||||
describe('redactHeaders', () => {
|
||||
test('when x-wp-nonce is present', () => {
|
||||
expect(redactHeaders({
|
||||
expect(
|
||||
redactHeaders({
|
||||
'X-WP-NONCE': 'abc123'
|
||||
})).toEqual({'X-WP-NONCE': 'REDACTED'})
|
||||
expect(redactHeaders({
|
||||
})
|
||||
).toEqual({ 'X-WP-NONCE': 'REDACTED' })
|
||||
expect(
|
||||
redactHeaders({
|
||||
'x-wp-nonce': 'abc123'
|
||||
})).toEqual({'x-wp-nonce': 'REDACTED'})
|
||||
expect(redactHeaders({
|
||||
})
|
||||
).toEqual({ 'x-wp-nonce': 'REDACTED' })
|
||||
expect(
|
||||
redactHeaders({
|
||||
'X-WP-Nonce': 'abc123'
|
||||
})).toEqual({'X-WP-Nonce': 'REDACTED'})
|
||||
})
|
||||
).toEqual({ 'X-WP-Nonce': 'REDACTED' })
|
||||
})
|
||||
})
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue